在多线程的使用过程中,需要考虑的一个问题就是线程安全。如果使用多线程对多个对象进行操作,也许不会有异常的情况的产生,因为每个对象是独立的,每个对象的改变对其他对象没有影响。但是如果使用多线程操作同一个对象,极有可能产生脏数据;但是同时访问同一个对象不会产生脏数据
多线程修改同一对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import threading import time
NUM = 5
def func(): global NUM NUM -= 1 time.sleep(1) print(NUM)
for i in range(5): t = threading.Thread(target=func) t.start() ------------ 00 0 0 0
|
上面的小程序中,定义了一个全局变量NUM
,在func
方法中对全局变量NUM
做自减一的操作,我们理想中的结果是
但实际结果却全都是0
这是因为每个线程运行到sleep(1)
的时候都在这里卡住,最后,所有的线程都执行完了NUM -= 1
,这个时候全局变量NUM
已经=0
,而第一个线程还没有执行完sleep(1)
,所以最后所有的线程打印出来的都是0
Lock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import threading import time
NUM = 5
def func(lock): global NUM
lock.acquire() NUM -= 1 time.sleep(1) print(NUM) lock.release()
lock = threading.Lock()
for i in range(5): t = threading.Thread(target=func, args=(lock,)) t.start() ------------ 4 3 2 1 0
|
RLock
RLock和Lock的使用方法相同,但却是Lock的”升级版”,RLock支持在函数内的多层嵌套锁,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import threading import time
NUM = 5
def func(lock): global NUM
lock.acquire() NUM -= 1
lock.acquire() time.sleep(1) lock.release()
print(NUM) lock.release()
lock = threading.RLock()
for i in range(5): t = threading.Thread(target=func, args=(lock,)) t.start()
|
Semaphore
信号量,可以放出一批锁,类似于批量“授权”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import threading import time
NUM = 5
def func(i, lock): global NUM
lock.acquire() NUM -= 1 time.sleep(1) print(i, NUM) lock.release()
lock = threading.BoundedSemaphore(3)
for i in range(5): t = threading.Thread(target=func, args=(i, lock,)) t.start() ------------ 0 2 1 2 2 2 4 0 3 0
|
从结果中可以看出,总共有3把锁被放出,所以前三个线程打印值是相同的,三个线程依次修改NUM
的值,然后依次进入time.sleep(1)
等待(此时NUM已经被自减了3次,值=2),最后依次打印出NUM
的值
event
事件, 同样是“批量授权”,但是和信号量指定创建锁的数量不同的是,事件要么锁住所有的线程,要么释放所有的线程,不能自定义锁的数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import threading
def func(i, event): print(i)
event.wait() print(i + 100)
event = threading.Event()
event.clear()
for i in range(5): t = threading.Thread(target=func, args=(i, event)) t.start()
inp = input("> ") if inp == "go": event.set() ------------ 0 1 2 3 4 > go 100 103 101 104 102
|
- wait:检测“Flag”的值
- clear:将“Flag”设置为False
- set:将“Flag”设置为True
Condition
条件,上面说了事件的特性,就是要阻塞就阻塞住所有的子线程,要释放就释放掉所有的锁,不支持信号量中的自定义释放锁的数量
条件锁,既支持事件阻塞所有子线程的特性,又具有信号量指定释放锁数量的特性
condition的第一种用法
指定释放锁的数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import threading
def func(i, con): print(i)
con.acquire() con.wait() print(i + 100) con.release()
condition = threading.Condition()
for i in range(5): t = threading.Thread(target=func, args=(i, condition)) t.start()
while True: inp = input("> ") if inp == "q": break
condition.acquire() condition.notify(int(inp)) condition.release() ------------ 0 1 2 3 4 > 1 > 100 2 > 102 101 3 > 104 103
|
最后只打印两个数字,是因为总共有5个测试数据,前面已经累计释放了三个子线程处理的测试数据,所以第三次,我即使指定了释放3个锁(当时程序中只剩两个子线程被锁住),也只会出现2个值
condition的第二种用法
如果条件为真,释放一个锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import threading
def select(): ret = False inp = input("> ") if inp == "true": ret = True else: ret = False return ret
def func(i, con): print(i)
con.acquire() con.wait_for(select) print(i + 100) con.release()
condition = threading.Condition()
for i in range(5): t = threading.Thread(target=func, args=(i, condition)) t.start() ------------ 0 > 1 2 3 4 true 100 > true 101 >
|
在con.wait_for(select)
中,我们传入了一个函数名,当第一个子线程执行到这一行的时候,会自动去执行这个函数,从而打印出了第一个>
符号,之后就停在了等待用户输入的那一行input()
其他所有的子线程都停在了con.acquare()
这一行,等待前面的子线程释放锁
这时,我输入一个true
,select
函数返回了一个True
, wait_for()
接收到这个True
之后,就继续向下执行,最后释放掉自己的锁
接着下一个被调度到的子线程执行到wait_for()
又去执行了里面传入的函数……以此类推
Timer
Timer不是锁,是定时器,用来指定x秒后执行某些操作
1 2 3 4 5 6 7 8 9 10 11 12
| from threading import Timer
def hello(): print("hello, world")
t = Timer(1, hello) t.start()
------------ hello, world
|
结果没有立即输入,而是等了一秒钟之后输出了hello, world