困于心衡于虑而后作
今天的学习目标是:python的线程等待和线程锁
1.线程等待
线程等待
在多线程的程序中往往一个线程(例如主线程)要等待其他线程执行完毕才继续执行,
这可以用join函数,使用的方法是:
线程对象.join()
在一个线程代码中执行这条语句,当前的线程就会停止执行,
一直等到指定的线程对象的线程执行完毕后才继续执行
即这条语句启动阻塞等待的作用
# 主线程启动子线程等待结束后再执行
import threading
import time
import random
def reading():
for i in range(5):
print('reading', i)
time.sleep(random.randint(1, 2))
t = threading.Thread(target=reading)
t.setDaemon(False)
t.start()
t.join()
print('the end')
# 由此可见主线程启动子线程t执行reading函数,t.join就会阻塞主线程,
# 一直等到t线程执行完毕后才结束t.join(),继续执行显示the end
运行结果:
# 子线程启动子线程等待结束后再执行
import threading
import time
import random
def reading():
for i in range(5):
print('reading ', i)
time.sleep(random.randint(1, 2))
def test():
r = threading.Thread(target=reading)
r.setDaemon(True)
r.start()
r.join()
print('test end')
t = threading.Thread(target=test)
t.setDaemon(False)
t.start()
t.join()
print('the end')
# 由此可见主线程启动t.join()会等待t线程结束,在test中再次启动r子线程,而且r.join()而阻塞t线程
# 等待r线程执行完毕后才结束r.join(),然后显示test end,之后t线程结束,再次结束t.join()
# 主线程显示the end后结束
运行结果:
2.线程锁
在多个线程的程序中有一个普遍存在的问题是,如果多个线程要竞争同时访问与改写公共资源
那么应该怎么样协调各个线程之间的关系,一个普遍使用的方法是使用线程锁,python使用threading.RLock类
来创建一个线程锁对象
lock=-threading.RLock()
这个对象lock有两个重要的方法是acquire()与release()
当执行:
lock.acquire()
语句强迫lock获取线程锁,
如果已经有了另外的线程先调用了acquire()方法获取了线程锁而还没有调用release()释放锁,
那么这个lock.acquire()就阻塞当前的线程,一直等待锁的控制权,
直到别的线程释放锁后lock.acquire()就获取锁并解除阻塞,线程继续执行
执行后线程要调用
lock.release()
释放锁,不然别的线程会一直得不到锁的控制权
使用acquire/release的工作机制我们可以把一段修改公共资源的代码用acquire()与release()夹起来,
这样就可以保证一次最多只有一个线程在修改公共资源
# 子线程a升序,子线程d降序
import threading
import time
lock = threading.RLock()
words = ['a', 'b', 'd', 'b', 'p', 'm', 'e', 'f', 'b']
def increase():
global words
for count in range(5):
lock.acquire()
print('a acquire')
for i in range(len(words)):
for j in range(i + 1, len(words)):
if words[j] < words[i]:
t = words[i]
words[i] = words[j]
words[j] = t # 这里需要注意,内容交换
print('a ', words)
time.sleep(1)
lock.release()
def decrease():
global words
for count in range(5):
lock.acquire()
print('d acquire')
for i in range(len(words)):
for j in range(i + 1, len(words)):
if words[j] > words[i]:
t = words[i]
words[i] = words[j]
words[j] = t
print('d ', words)
time.sleep(1)
lock.release()
a = threading.Thread(target=increase)
a.setDaemon(False)
a.start()
d = threading.Thread(target=decrease)
d.setDaemon(False)
d.start()
print('the end')
# 由此可见,无论是increase还是decrease的排序过程,都是在获得锁的控制权下进行的,
# 因此排序过程中另一个线程必然处于等待状态,不会干扰本次排序,因此每次显示的结果不是升序就是降序的
运行结果:
# 子线程a升序,子线程d降序,使用线程锁不混乱
import threading
import time
lock = threading.RLock()
words = ['a', 'b', 'd', 'b', 'p', 'm', 'e', 'f', 'b']
s = ['a', 'b', 'c', 'd']
def asort():
global s
lock.acquire()
for i in range(len(s)):
for j in range(i + 1, len(s)):
if s[i] > s[j]:
t = s[i]
s[i] = s[j]
s[j] = t
time.sleep(0.1)
print(s)
lock.release()
def dsort():
global s
lock.acquire()
for i in range(len(s)):
for j in range(i + 1, len(s)):
if s[j] > s[i]:
t = s[i]
s[i] = s[j]
s[j] = t
time.sleep(0.1)
print(s)
lock.release()
def show():
global s
for e in s:
print(e, end=' ')
print()
show()
for i in range(5):
a = threading.Thread(target=asort)
a.setDaemon(False)
d = threading.Thread(target=dsort)
d.setDaemon(False)
a.start()
d.start()
# a.join()
# d.join()
show()
print('the end')
# 只有当上一次的线程的lock释放出来后,下一个线程才能获取线程锁,执行资源操作,
# 即要么执行从大到小的排序,要么执行从小到大的排序,资源操作不是同时执行的,资源的排序不会出现混乱的状态