(1)线程之间虽然数据共享,但是多个线程同时修改同一份数据,依然会造成数据的不准确,因此,也需要加
上线程锁Lock,用法和进程锁一样,只不过是从threading里面导包
# (1)常见情况引发的死锁问题,模拟4个人吃一碗面 from threading import Thread,Lock import time noodle_lock = Lock() kuaizi_lock = Lock() def eat1(name): noodle_lock.acquire() print("%s拿到这个面条" % (name)) kuaizi_lock.acquire() print("%s拿到了筷子" % (name)) print("开始吃") time.sleep(0.5) kuaizi_lock.release() print("%s把筷子放下"% (name)) noodle_lock.release() print("%s把面放下"% (name)) def eat2(name): kuaizi_lock.acquire() print("%s拿到了筷子" % (name)) noodle_lock.acquire() print("%s拿到这个面条" % (name)) print("开始吃") time.sleep(0.5) noodle_lock.release() print("%s把面放下"% (name)) kuaizi_lock.release() print("%s把筷子放下"% (name)) if __name__ == "__main__": name_list = ["张楠","定海呀"] name_list2 = ["晨露中","周金波"] for name in name_list: Thread(target=eat1,args= (name,) ).start() for name in name_list2: Thread(target=eat2,args = (name,)).start()
执行结果
张楠拿到这个面条
张楠拿到了筷子
开始吃
张楠把筷子放下
晨露中拿到了筷子
张楠把面放下定海呀拿到这个面条
运行完,发现这并不是我们想要的结果,一个人很难同时拿到面和筷子
(2)不要因为逻辑问题让上锁分成两次.导致死锁,递归锁用于解决死锁,但只是一种应急的处理办法,关键
时刻可以防止服务器崩溃,递归锁:RLock:递归所专门用来解决死锁现象的
只要第一个进程拿到锁,该进程往下的锁全部打开,等他走完了,再放下一个进程进来
from threading import Thread, RLock import time noodle_lock = kuaizi_lock = RLock() def eat1(name): noodle_lock.acquire() print("%s拿到这个面条" % (name)) kuaizi_lock.acquire() print("%s拿到了筷子" % (name)) print("开始吃") time.sleep(0.5) kuaizi_lock.release() print("%s把筷子放下" % (name)) noodle_lock.release() print("%s把面放下" % (name)) def eat2(name): kuaizi_lock.acquire() print("%s拿到了筷子" % (name)) noodle_lock.acquire() print("%s拿到这个面条" % (name)) print("开始吃") time.sleep(0.5) noodle_lock.release() print("%s把面放下" % (name)) kuaizi_lock.release() print("%s把筷子放下" % (name)) if __name__ == "__main__": name_list = ["张楠", "定海呀"] name_list2 = ["晨露中", "周金波"] for name in name_list: Thread(target=eat1, args=(name,)).start() for name in name_list2: Thread(target=eat2, args=(name,)).start()
张楠拿到这个面条
张楠拿到了筷子
开始吃
张楠把筷子放下
张楠把面放下
定海呀拿到这个面条
定海呀拿到了筷子
开始吃
定海呀把筷子放下
定海呀把面放下
晨露中拿到了筷子
晨露中拿到这个面条
开始吃
晨露中把面放下
周金波拿到了筷子
周金波拿到这个面条
开始吃
晨露中把筷子放下
周金波把面放下
周金波把筷子放下
执行结果,会发现是不是基本完美了
(3)从语法上说,锁是允许互相嵌套的,但是不要使用;
上一次锁,就对应解开一次,形成互斥锁
吃面和拿筷子是同时的,不要因为逻辑问题让锁分别上,容易造成死锁.
from threading import Thread, Lock import time mylock = Lock() def eat1(name): mylock.acquire() print("%s拿到筷子" % (name)) print("%s吃到面条" % (name)) time.sleep(0.5) print("%s放下筷子" % (name)) print("%s放下面条" % (name)) mylock.release() def eat2(name): mylock.acquire() print("%s拿到筷子" % (name)) print("%s吃到面条" % (name)) time.sleep(0.5) print("%s放下筷子" % (name)) print("%s放下面条" % (name)) mylock.release() if __name__ == "__main__": name_list = ["张楠", "定海呀"] name_list2 = ["晨露中", "周金波"] for name in name_list: Thread(target=eat1, args=(name,)).start() for name in name_list2: Thread(target=eat2, args=(name,)).start()
张楠拿到筷子
张楠吃到面条
张楠放下筷子
张楠放下面条
定海呀拿到筷子
定海呀吃到面条
定海呀放下筷子
定海呀放下面条
晨露中拿到筷子
晨露中吃到面条
晨露中放下筷子
晨露中放下面条
周金波拿到筷子
周金波吃到面条
周金波放下筷子
周金波放下面条
执行结果,这样写是不是看起来更清楚!
(4)线程中的信号量Semaphore
进程而言:
信号量是配合Process使用,可以限制并发的进程数
在有了进程池之后,可以取代如上做法
线程而言:
信号量是配合Thread使用的,可以限制并发的线程数
线程池可以取代如上用法
from threading import Semaphore,Thread import time start = time.time() def func(index,sem): sem.acquire() print(index) # time.sleep(1) sem.release() if __name__ == "__main__": sem = Semaphore(5) for i in range(16): Thread(target=func,args=(i,sem)).start() end = time.time() time_ = end - start print(time_)
执行结果:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0.0060002803802490234