1. 线程锁 Lock,得到一个锁对象 lock,可以用 lock.acquire() 将全局变量锁起来,只允许当前上锁的线程操作;再调用 lock.release() 解锁,让其他线程有机会操作这个全局变量。
from threading import Lock
total = 0
lock = Lock()
def add():
global lock
global total
for i in range(100000):
lock.acquire() # 上锁
total += 1
lock.release() # 解锁
def desc():
global lock
global total
for i in range(100000):
lock.acquire() # 上锁
total -= 1
lock.release() # 解锁
import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
2. 可重复调用的锁 RLock,RLock是一种可重入的锁,第一个锁没有释放,第二个也能获取到锁。但要注意的一点是,acquire的次数要和release的次数相等,否则引起死锁。
from threading import RLock
total = 0
lock = RLock()
def add():
global lock
global total
for i in range(1000000):
lock.acquire()
lock.acquire() # 可重入的锁
total += 1
lock.release()
lock.release()
def desc():
global total
global lock
for i in range(1000000):
lock.acquire()
total -= 1
lock.release()
import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
3. 条件变量 Condition,除了Lock带有的锁定池外,Condition 还包含一个等待池,池中的线程处于状态图中的等待阻塞状态,直到另一个线程调用 notify()/notifyAll() 通知,得到通知后线程进入锁定池等待锁定。用 Condition时要注意,.wait() 和 .notify() 方法顺序要安排好,要先 wait()再notify(),不然会导致死锁。
import threading
class XiaoAi(threading.Thread):
def __init__(self, cond):
super().__init__(name="小爱")
self.cond = cond
def run(self):
with self.cond:
self.cond.wait()
print("{} : 在 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 好啊 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 君住长江尾 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 共饮长江水 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 此恨何时已 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 定不负相思意 ".format(self.name))
self.cond.notify()
class TianMao(threading.Thread):
def __init__(self, cond):
super().__init__(name="天猫精灵")
self.cond = cond
def run(self):
with self.cond:
print("{} : 小爱同学 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 我们来对古诗吧 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 我住长江头 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 日日思君不见君 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 此水几时休 ".format(self.name))
self.cond.notify()
self.cond.wait()
print("{} : 只愿君心似我心 ".format(self.name))
self.cond.notify()
self.cond.wait()
if __name__ == "__main__":
from concurrent import futures
cond = threading.Condition()
# 用同一把锁传给两个不同的线程
xiaoai = XiaoAi(cond)
tianmao = TianMao(cond)
#启动顺序很重要
#在调用with cond 之后才能调用 wait 或者 notify 方法
#condition 有两层锁, 一把底层锁会在线程调用了 wait 方法的时候释放, 上面的锁会在每次调用 wait 的时候分配一把并放入到 cond 的等待队列中,等到 notify 方法的唤醒
xiaoai.start()
tianmao.start()
4. 信号量 Semaphore,semaphore是一个内置的计数器,计数器不能小于0,当计数器为0时,acquire() 将阻塞线程直到其他线程调用 release()。可以用来控制进入代码段的程序数量。比如,文件读写, 写一般只是用于一个线程写,读可以允许有多个。
import threading
import time
class HtmlSpider(threading.Thread):
def __init__(self, url, sem):
super().__init__()
self.url = url
self.sem = sem
def run(self):
time.sleep(2)
print("got html text success")
self.sem.release() # release 时,会将数量加回来
class UrlProducer(threading.Thread):
def __init__(self, sem):
super().__init__()
self.sem = sem
def run(self):
for i in range(20):
self.sem.acquire() # 每调用一次 acquire ,会将数量减1,减到为0则进不去
html_thread = HtmlSpider("https://baidu.com/{}".format(i), self.sem)
html_thread.start()
if __name__ == "__main__":
sem = threading.Semaphore(3) # 信号量
url_producer = UrlProducer(sem)
url_producer.start()
本文参考文章:
python中的线程之semaphore信号量
python笔记11-多线程之Condition(条件变量)