先上个例子:
import threading
def 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 threading
import time
a = threading.local()#全局对象
def worker():
a.x = 0
for i in range(100):
time.sleep(0.0001)
a.x += 1
print(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, time
def 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, time
import random
def light():
if not event.isSet(): #初始化evet的flag为真
event.set() #wait就不阻塞 #绿灯状态
count = 0
while 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 = 0
event.set() #打开绿灯
time.sleep(1)
count += 1
def 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,即输入的资源)没来,哪种方式都是瞎折腾。