import time
import threading
lock = threading.RLock()
n = 10
def task(arg):
# 加锁,此区域的代码同一时刻只能有一个线程执行
lock.acquire()
# 获取当前线程对象
thread_obj = threading.current_thread()
# 获取当前线程名字
name = thread_obj.getName()
global n
n = arg
time.sleep(1)
print('当前线程', name, '修改后n的值为:', n)
# 释放锁
lock.release()
for i in range(5):
t = threading.Thread(target=task, args=(i,))
t.setName(str(i))
t.start()
'''
期望结果-->加锁情况:
当前线程 0 修改后n的值为: 0
当前线程 1 修改后n的值为: 1
当前线程 2 修改后n的值为: 2
当前线程 3 修改后n的值为: 3
当前线程 4 修改后n的值为: 4
'''
'''
不期望结果-->没加锁情况
当前线程 0 修改后n的值为: 4
当前线程 1 修改后n的值为: 4
当前线程 2 修改后n的值为: 4
当前线程 3 修改后n的值为: 4
当前线程 4 修改后n的值为: 4
'''
引子:为什么要加锁?
需求:每个线程将一个数字添加到列表,然后取出自己放的数字,线程结束.
希望得到的结果如下:
'''
0 0
1 1
2 2
3 3
4 4
'''
代码:
import threading
import time
lst = []
def func(arg):
# 线程安全
lst.append(arg)
time.sleep(0.1)
m = lst[-1]
print(arg,m)
for i in range(5):
t = threading.Thread(target=func,args=(i,))
t.start()
总结:
从上面这个例子看,如果不加锁,每个线程放进去自己的数字,再取最后一个数字,就不一定是自己放的,因为这个时间,可能其他线程也放进去了,你拿到的,可能是其他线程放的.所以这个时候就需要加锁,限制一个线程没操作完,另一个绝对不能动.
1.锁:Lock(1次放1个)
import threading
import time
lst = []
lock = threading.Lock()
def func(arg):
lock.acquire()
lst.append(arg)
time.sleep(0.1)
m = lst[-1]
lock.release()
for i in range(5):
t = threading.Thread(target=func,args=(i,))
t.start()
2.锁:RLock(1次放1个)
import threading
import time
lst = []
lock = threading.RLock()
def func(arg):
lock.acquire()
lock.acquire()
lst.append(arg)
time.sleep(0.1)
m = lst[-1]
print(arg, m)
lock.release()
lock.release()
for i in range(5):
t = threading.Thread(target=func, args=(i,))
t.start()
PS:Lock和RLock的区别是RLock可以多次加锁.
3.锁:BoundedSemaphore(1次放N个)信号量
import time
import threading
lst = []
# 一次放3个
lock = threading.BoundedSemaphore(3)
def func(arg):
lock.acquire()
lst.append(arg)
m = lst[-1]
print(arg, m)
time.sleep(3)
lock.release()
for i in range(10):
t = threading.Thread(target=func, args=(i,))
t.start()
4.锁:Condition(1次放x个)
方法1:
import time
import threading
lst = []
lock = threading.Condition()
def func(arg):
print('start...')
lock.acquire()
lock.wait()
lst.append(arg)
m = lst[-1]
print(arg, m)
time.sleep(1)
lock.release()
for i in range(10):
t = threading.Thread(target=func, args=(i,))
t.start()
while True:
num = int(input('>>>:'))
lock.acquire()
# 控制放几个线程执行,比如写3个,但是线程实际2个,那就是2个了
lock.notify(num) # 3
lock.release()
方法2:
import time
import threading
lock = threading.Condition()
def func1():
print('start...')
input('>>>:')
return True
def func2(arg):
print('\n线程进来了')
# func1作为执行条件
lock.wait_for(func1)
print(arg)
time.sleep(1)
for i in range(10):
t = threading.Thread(target=func2, args=(i,))
t.start()
5.锁:Event(1次放所有)
import threading
lock = threading.Event()
def func(arg):
print('线程来了')
# 加锁:红灯
lock.wait()
print(arg)
for i in range(10):
t = threading.Thread(target=func, args=(i,))
t.start()
input('>>>:')
lock.set() # 绿灯
lock.clear() # 再次变红灯
for i in range(10):
t = threading.Thread(target=func, args=(i,))
t.start()
input('>>>')
lock.set()
总结:
线程安全,列表和字典线程安全;
为什么要加锁?
- 非线程安全
- 控制一段代码