标签:单位 解释器 存在 遇到 影响 join() 另一个 理解 有关
多线程类似同时执行多个不同的程序,每个独立的线程都有一个程序的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。线程的概念理解起来可能比较抽象,它是操作系统能够进行运算调度的最小单位。本质上是一串指令的集合。
在python3中,我们使用threading模块来支持多线程,那么我们先来写一个最简单的多线程:
import threading
import time
def run1():
print('我是第一个线程')
time.sleep(2)
def run2():
print('我是第二个线程')
time.sleep(2)
start_time=time.time()
t1=threading.Thread(target=run1)
t2=threading.Thread(target=run2)
t1.start()
t2.start()
t1.join()
t2.join()
end_time=time.time()
print(end_time-start_time)
output:
我是第一个线程
我是第二个线程
2.0031144
在这个实例中,我们一步步来解析threading模块的作用:
当然有人会好奇,这样比不用多线程真的快吗?我们来试试利用单线程完成这个程序:
import time
def run1():
print('我是第一个线程')
time.sleep(2)
def run2():
print('我是第二个线程')
time.sleep(2)
start_time=time.time()
run1()
run2()
end_time=time.time()
print(end_time-start_time)
output:
我是第一个线程
我是第二个线程
4.00122
很明显可以看到利用多线程比单线程确实有很大优势。
另外,多线程中对于子线程的处理还有一种方法叫做守护线程,所谓守护线程,就是当该程序的主线程结束时,子线程也全部强制结束。我们再写个简单的例子:
import threading,time
def run1():
print('第一个功能')
time.sleep(1)
print('第二个功能')
t1=threading.Thread(target=run1)
t1.setDaemon(True)
t1.start()
print('主线程结束了')
output:
第一个功能
主线程结束了
设置子线程守护的方法为setDaemon方法,线程调用该方法并传入参数True,即可设置该线程为守护线程。
在多线程任务中,还存在一些问题。多线程在程序中是共享变量资源的,在不同线程同时对同一个变量进行修改的时候,可能就会遇到一些未知的错误。此时,就需要引入另一个功能模块,叫做互斥锁。互斥锁的原理是将最核心的变量资源加锁,在一个线程对该资源进行操作时,其余线程不得访问该资源。 在threading模块中定义了Lock类
import threading
lock=threading.lock()
那么我们该如何利用这个锁尼?
import threading
lock=threading.lock() #创建锁
lock.acquire() #上锁
#对变量(资源)进行访问、修改
lock.release() # 解锁 修改完成后一定要解锁 不让会出现死锁现象
这里要注意的是在使用完毕后要及时释放资源,否则会导致死锁现象。
其实,通过这个例子可以看出来,一旦引入了互斥锁,会发现这个多线程真正意义上来说是一种伪多线程,它不能做到真正的多个线程同时进行,这和python的底层解释器GIL有关。
这里引入的GIL,称为全局解释器锁。Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。
对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果某线程并未使用很多I/O操作,它会在自己的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。
那么我们要如何改善python的多线程,真正实现多核的利用尼?
第一个:更换底层GIL解释器。这个对于初学者来说了解得比较少,不建议这么做。
第二个:利用python的多进程来替换多线程,不同python进程中有各自独立的GIL锁,互不影响。
第三个:利用C语言来写计算密集型任务,然后把.so链接库内容加载到python中,因为执行C代码,GIL锁会释放,这样可以做到每个核跑一个线程。
所以,建议各位小伙伴们在掌握python后,再学一下C语言。
标签:单位 解释器 存在 遇到 影响 join() 另一个 理解 有关
原文地址:https://www.cnblogs.com/magicdata/p/12295852.html