问题:
引出问题:
下面代码的执行结果不为0
total = 0
def add():
global total
for i in range(10000000):
total += 1
def desc():
global total
for i in range(10000000):
total -= 1
import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
解决方法:
1)线程同步工具-锁 lock
lock 枷锁后线程会交替进行加减操作。
from threading import Lock
total = 0
lock = Lock()
def add():
global total
global lock
for i in range(10000000):
lock.acquire()
total += 1
lock.release()
def desc():
global total
global lock
for i in range(10000000):
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)
-------------------------
0 #执行结果恒为0
加锁后的影响:
1)锁后会影响代码运行性能
2)锁有可能引起死锁(未释放,多次获取,互相等待)
2)线程同步工具-可重入的锁 RLock
特性
- 在同一个线程里面可以联系acquire多次(acquire 与 release次数相同)
from threading import Lock ,RLock
total = 0
lock = RLock()
def add():
global total
global RLock
for i in range(10000000):
lock.acquire()
lock.acquire()
lock.acquire()
lock.acquire()
lock.acquire()
total += 1
lock.release()
lock.release()
lock.release()
lock.release()
lock.release()
def desc():
global total
global lock
for i in range(10000000):
lock.acquire()
lock.acquire()
lock.acquire()
lock.acquire()
lock.acquire()
total -= 1
lock.release()
lock.release()
lock.release()
lock.release()
lock.release()
import threading
thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
-------------------------------------
0 #结果恒为0
条件变量:
类似两个人之间的交流功能,先用lock试下
import threading
class XIAOAITONGXUE(threading.Thread):
def __init__(self,lock):
super().__init__(name='小爱')
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:在".format(self.name))
self.lock.release()
class TIANMAOJINGLING(threading.Thread):
def __init__(self,lock):
super().__init__(name='天猫精灵')
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:小爱同学 ".format(self.name))
self.lock.release()
if __name__ == '__main__':
lock = threading.Lock()
tianmo = TIANMAOJINGLING(lock)
xia = XIAOAITONGXUE(lock)
tianmo.start()
xia.start()
-------------------------------------------------------
天猫精灵:小爱同学
小爱:在
但是当我们想让他们实现对话的时候
import threading
class XIAOAITONGXUE(threading.Thread):
def __init__(self,lock):
super().__init__(name='小爱')
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:在".format(self.name))
self.lock.release()
self.lock.acquire()
print("{}:好呀".format(self.name))
self.lock.release()
class TIANMAOJINGLING(threading.Thread):
def __init__(self,lock):
super().__init__(name='天猫精灵')
self.lock = lock
def run(self):
self.lock.acquire()
print("{}:小爱同学 ".format(self.name))
self.lock.release()
self.lock.acquire()
print("{}:我们来对诗吧 ".format(self.name))
self.lock.release()
if __name__ == '__main__':
lock = threading.Lock()
tianmo = TIANMAOJINGLING(lock)
xia = XIAOAITONGXUE(lock)
tianmo.start()
xia.start()
--------------------------------------------------------
天猫精灵:小爱同学
天猫精灵:我们来对诗吧
小爱:在
小爱:好呀
没有实现两个线程的交互
通过condition来完成:
condition中实现了enter exit协议,可以使用with语句
wait等待某个条件变量的通知
notify通知启动
condition有两层锁,一把底层锁会在线程调用wait方法时释放,上面的锁会在每次调用wait时重新分配一把并放到cond的等待队列中,等待notify方法的唤醒。
import threading
from threading import Condition
class XIAOAITONGXUE(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() # 通知说话
class TIANMAOJINGLING(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() # 等待
if __name__ == '__main__':
cond = Condition()
tianmo = TIANMAOJINGLING(cond)
xia = XIAOAITONGXUE(cond)
#注意启动顺序,如果先启动tianmo会notify通知xia,但是xia还没有启动
#wait 与 notify 一定是调用condition之后才能使用。
xia.start()
tianmo.start()
-----------------------------------------------------
天猫精灵:小爱同学
小爱:在
天猫精灵:我们来对诗吧
小爱:好的呀
天猫精灵:鹅鹅鹅
小爱:曲项向天歌
天猫精灵:白毛浮绿水
小爱:红掌拨清波
Semaphore限制并发数
import threading,time
from threading import Semaphore
class HtmlSpider(threading.Thread):
def __init__(self,url,sem):
super().__init__()
self.url = url
self.sem = sem
def run(self):
time.sleep(2)
print(self.url,)
#运行完成 释放
self.sem.release()
class UrlProducer(threading.Thread):
def __init__(self,sem):
super().__init__()
#传入一个Semaphore
self.sem = sem
def run(self):
for i in range(30):
#Semaphore内有 acquire release
self.sem.acquire()
html_thread = HtmlSpider("https://baidu.com/{id}".format(id=i),self.sem)
html_thread.start()
if __name__ == '__main__':
sem = threading.Semaphore(3)
url = UrlProducer(sem)
url.start()