Python程序中的线程操作-锁
目录
一、同步锁
1.1 多个线程抢占资源的情况
from threading import Thread
import os,time
def work():
global n
temp=n
time.sleep(0.1)
n=temp-1
if __name__ == '__main__':
n=100
l=[]
for i in range(100):
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
print(n)
结果可能为: 99
1.1.1 对公共数据的操作
import threading
R = threading.Lock()
R.acquire()
'''
对公共数据的操作
'''
R.release()
1.2 同步锁的引用
from threading import Thread, Lock
import os, time
def work():
global n
lock.acquire()
temp = n
time.sleep(0.1)
n = temp - 1
lock.release()
if __name__ == '__main__':
lock = Lock()
n = 100
l = []
for i in range(100):
p = Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
print(n) # 结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
结果肯定为:0
1.3 互斥锁与join的区别
#不加锁:并发执行,速度快,数据不安全
from threading import current_thread, Thread, Lock
import time
def task():
global n
print('%s is running' % current_thread().getName())
temp = n
time.sleep(0.5)
n = temp - 1
if __name__ == '__main__':
n = 100
lock = Lock()
threads = []
start_time = time.time()
for i in range(100):
t = Thread(target=task)
threads.append(t)
t.start()
for t in threads:
t.join()
stop_time = time.time()
print('主:%s n:%s' % (stop_time - start_time, n))
'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:0.5216062068939209 n:99
'''
#不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
from threading import current_thread, Thread, Lock
import time
def task():
# 未加锁的代码并发运行
time.sleep(3)
print('%s start to run' % current_thread().getName())
global n
# 加锁的代码串行运行
lock.acquire()
temp = n
time.sleep(0.5)
n = temp - 1
lock.release()
if __name__ == '__main__':
n = 100
lock = Lock()
threads = []
start_time = time.time()
for i in range(100):
t = Thread(target=task)
threads.append(t)
t.start()
for t in threads:
t.join()
stop_time = time.time()
print('主:%s n:%s' % (stop_time - start_time, n))
'''
Thread-1 is running
Thread-2 is running
......
Thread-100 is running
主:53.294203758239746 n:0
'''
# 有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
# 没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
# start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
# 单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
from threading import current_thread, Thread, Lock
import time
def task():
time.sleep(3)
print('%s start to run' % current_thread().getName())
global n
temp = n
time.sleep(0.5)
n = temp - 1
if __name__ == '__main__':
n = 100
lock = Lock()
start_time = time.time()
for i in range(100):
t = Thread(target=task)
t.start()
t.join()
stop_time = time.time()
print('主:%s n:%s' % (stop_time - start_time, n))
'''
Thread-1 start to run
Thread-2 start to run
......
Thread-100 start to run
主:350.6937336921692 n:0 #耗时是多么的恐怖
'''
)
二、死锁与递归锁
进程也有死锁与递归锁,在进程那里忘记说了,放到这里一起说了。
所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
2.1 死锁
from threading import Lock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()
解决方法:递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁。
2.2 递归锁RLock
from threading import RLock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()
三、典型问题:科学家吃面
3.1 死锁问题
import time
from threading import Thread, Lock
noodle_lock = Lock()
fork_lock = Lock()
def eat1(name):
noodle_lock.acquire()
print('%s 抢到了面条' % name)
fork_lock.acquire()
print('%s 抢到了叉子' % name)
print('%s 吃面' % name)
fork_lock.release()
noodle_lock.release()
def eat2(name):
fork_lock.acquire()
print('%s 抢到了叉子' % name)
time.sleep(1)
noodle_lock.acquire()
print('%s 抢到了面条' % name)
print('%s 吃面' % name)
noodle_lock.release()
fork_lock.release()
for name in ['哪吒', 'randy', 'laowang']:
t1 = Thread(target=eat1, args=(name,))
t2 = Thread(target=eat2, args=(name,))
t1.start()
t2.start()
"""
1. 线程1首先运行过了eat1,接着运行eat2拿到锁二,往下运行需要fork_lock这把锁,
2. 线程2首先运行到eat1,也拿到了 fork_lock这把锁,接着往下运行发现资源都被锁住,两把锁都没有释放资源,两者都拿到彼此向下运行的必要条件,两个线程都不放手里的锁头,导致死锁
"""
哪吒 抢到了面条
哪吒 抢到了叉子
哪吒 吃面
哪吒 抢到了叉子
randy 抢到了面条
卡住,不在往下执行,出现了死锁
3.2 递归锁解决死锁问题
import time
from threading import Thread, RLock
fork_lock = noodle_lock = RLock()
def eat1(name):
noodle_lock.acquire()
print('%s 抢到了面条' % name)
fork_lock.acquire()
print('%s 抢到了叉子' % name)
print('%s 吃面' % name)
fork_lock.release()
noodle_lock.release()
def eat2(name):
fork_lock.acquire()
print('%s 抢到了叉子' % name)
time.sleep(1)
noodle_lock.acquire()
print('%s 抢到了面条' % name)
print('%s 吃面' % name)
noodle_lock.release()
fork_lock.release()
for name in ['哪吒', 'randy', 'laowang']:
t1 = Thread(target=eat1, args=(name,))
t2 = Thread(target=eat2, args=(name,))
t1.start()
t2.start()
哪吒 抢到了面条
哪吒 抢到了叉子
哪吒 吃面
哪吒 抢到了叉子
哪吒 抢到了面条
哪吒 吃面
randy 抢到了面条
randy 抢到了叉子
randy 吃面
randy 抢到了叉子
randy 抢到了面条
randy 吃面
laowang 抢到了面条
laowang 抢到了叉子
laowang 吃面
laowang 抢到了叉子
laowang 抢到了面条
laowang 吃面