初学python的多线程,有一道经典的面试题,对于很多和我一样的初学者来说可能开始都不容易理解和掌握。
分享一下自己的理解和代码。重点是***的部分,希望对大家有帮助!
问:生成5个线程按照顺序来输出1234512345... 。其中1为线程1输出,2为线程2输出以此类推。
思路:使用Condition
解答:
import threading
cond = threading.Condition() # 利用Condition
#
def domywork(threadnum):
print(str(threadnum), end='')
# 构建一个自己的线程类
class mythread(threading.Thread):
def __init__(self, threadnum, thdcnt):
threading.Thread.__init__(self) # 父类初始化
self.thdnum = threadnum
self.thdcnt = thdcnt
def run(self) -> None:
global nowthdnum
jobcnt = 10 # 这里循环了10次,也可以放到mythread类的参数里,自己做做
for i in range(jobcnt):
with cond:
# *** 这里是重点1:因为系统调度线程执行时是任意的,即处于就绪状态的线程都可能被调度执行 ***
# 所以,执行到这里时,判断一下执行中的线程如果不等于nowthdnum(即没有按指定顺序执行时,让这个线程等待)
while nowthdnum != self.thdnum:
cond.wait()
# print('-- nowthdnum = ', nowthdnum) # Debug用
# *** 这里是重点2:控制应该轮到的下一个线程(一共开了thrdcnt个线程,所以模thrdcnt)来执行 ***
# 注意:这里线程号要从1开始,即:mythread(i+1, thdcnt)
nowthdnum = (nowthdnum % thdcnt) + 1
# 做自己的处理
domywork(self.thdnum)
# *** 这里是重点3:通知其他所有线程 ***
cond.notify_all()
# --------------- 主程序开始 ------------------------
mthdlist = []
thdcnt = 5 # 指定产生线程的数量
nowthdnum = 1 # *** 全局变量,用于控制下一个执行的线程,对比正在执行的线程号(threadnum)
for i in range(thdcnt):
mthd = mythread(i+1, thdcnt)
mthdlist.append(mthd)
mthd.start()
for i in mthdlist:
mthd.join()
print('\nfinished')
基本原理:
执行中的线程拿到锁后,先检查是不是符合执行的条件。比如打印“1”的线程,执行条件是 nowthdnum也为1,然后打印“1”,
随后将nowthdnum改为2,这样就成了下一个线程2能够执行的条件,而阻塞了其他的线程,让它们只能等待,以此类推。
因为这里不能通知指定的线程,所以使用notify_all通知所有的线程。