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()

总结:
线程安全,列表和字典线程安全;
为什么要加锁?
- 非线程安全
- 控制一段代码