先上个例子:
import threadingdef run():for i in range(100):print(threading.current_thread().name+"--------"+str(i))print(threading.current_thread().name)t1=threading.Thread(target=run,name="线程1")t2=threading.Thread(target=run,name="线程2")t1.start()# 立马执行t1, 权限高于主线程,执行完才会执行其他的线程#t1.join()t2.start()
线程间的通信:
线程锁:
#一个线程上锁,只有释放锁后,其他的线程才有执行权lock=threading.Lock()def run():lock.acquire()...lock.release()
多线程之间数据不共享:
'''Python提供了 threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其它线程不可见(本质上就是不同的线程使用这个对象时为其创建一个独立的字典)。'''#tips:存取值不要在主线程中进行,否则子线程访问不到属性import threadingimport timea = threading.local()#全局对象def worker():a.x = 0for i in range(100):time.sleep(0.0001)a.x += 1print(threading.current_thread(),a.x)for i in range(10):threading.Thread(target=worker).start()
线程调度:
- acquire()/release():获得/释放 Lock
- wait([timeout]):线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。调用wait()会释放Lock,直至该线程被Notify()、NotifyAll()或者超时线程又重新获得Lock.
- notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。
- notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)
'''现在写个捉迷藏的游戏来具体介绍threading.Condition的基本使用。假设这个游戏由两个人来玩,一个藏(Hider),一个找(Seeker)。游戏的规则如下:1. 游戏开始之后,Seeker先把自己眼睛蒙上,蒙上眼睛后,就通知Hider;2. Hider接收通知后开始找地方将自己藏起来,藏好之后,再通知Seeker可以找了;3. Seeker接收到通知之后,就开始找Hider。Hider和Seeker都是独立的个体,在程序中用两个独立的线程来表示,在游戏过程中,两者之间的行为有一定的时序关系,我们通过Condition来控制这种时序关系。'''import threading, timedef Seeker(cond, name):time.sleep(2)cond.acquire()print('%s :我已经把眼睛蒙上了!'% name)cond.notify()cond.wait()for i in range(3):print('%s is finding!!!'% name)time.sleep(2)cond.notify()cond.release()print('%s :我赢了!'% name)def Hider(cond, name):cond.acquire()cond.wait()for i in range(2):print('%s is hiding!!!'% name)time.sleep(3)print('%s :我已经藏好了,你快来找我吧!'% name)cond.notify()cond.wait()cond.release()print('%s :被你找到了,唉~^~!'% name)if __name__ == '__main__':cond = threading.Condition()seeker = threading.Thread(target=Seeker, args=(cond, 'seeker'))hider = threading.Thread(target=Hider, args=(cond, 'hider'))seeker.start()hider.start()
线程事件
- event = threading.Event()
- event.set()
- event.clear():将event.set()清除
- event.wait():只有event.set()之后才执行
import threading, timeimport randomdef light():if not event.isSet(): #初始化evet的flag为真event.set() #wait就不阻塞 #绿灯状态count = 0while True:if count < 10:print('\033[42;1m---green light on---\033[0m')elif count < 13:print('\033[43;1m---yellow light on---\033[0m')elif count < 20:if event.isSet():event.clear()print('\033[41;1m---red light on---\033[0m')else:count = 0event.set() #打开绿灯time.sleep(1)count += 1def car(n):while 1:time.sleep(random.randrange(3, 10))#print(event.isSet())if event.isSet():print("car [%s] is running..." % n)else:print('car [%s] is waiting for the red light...' % n)event.wait() #红灯状态下调用wait方法阻塞,汽车等待状态if __name__ == '__main__':car_list = ['BMW', 'AUDI', 'SANTANA']event = threading.Event()Light = threading.Thread(target=light)Light.start()for i in car_list:t = threading.Thread(target=car, args=(i,))t.start()
线程池:ThreadPoolExecutor
https://www.yuque.com/jiangwei-3zuwt/lyigmh/tf8ufs
介绍python的线程,需要介绍GIL锁的概念:
GIL不是Python特性
GIL是Python解释器(Cpython)时引入的概念,在JPython、PyPy中没有GIL。GIL并不是Python的语言缺陷。
GIL定义
GIL,the Global Interpreter Lock,直译为“全局解释锁”
GIL存在原因
CPython在执行多线程的时候并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有一个Python线程执行。
GIL的弊端
- GIL对计算密集型的程序会产生影响。因为计算密集型的程序,需要占用系统资源。GIL的存在,相当于始终在进行单线程运算,这样自然就慢了。
- IO密集型影响不大的原因在于,IO,input/output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待,所以它们是多个一起等(多线程)还是单个等(单线程)无所谓的。
这就好比,你在公交站等公交时,你们排队等公交(单线程)还是沿着马路一字排开等(多线程)是无所谓的。公交车(即input,即输入的资源)没来,哪种方式都是瞎折腾。
