Python 3,多线程 - 当每个线程完成时将数据传入主模块
下面的代码运行了两个线程(多线程),每个线程的延迟时间不同,这样每个线程完成的时间也就不一样。
一旦两个线程都完成,模块 display1.py 会打印一条信息,告诉我们它们都完成了。
我希望模块 display1.py 能在每个线程完成时,分别打印出“完成”的信息
我该怎么做呢……如果能对我现有的代码做一些修改就太好了!我希望尽量少改动现在的代码,所以我可能需要一种更好的方式来在这两个模块之间传递变量。
display1.py
from threads1 import *
manager = ThreadManager()
manager.start(False)
print (manager.GetResults())
threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
pass
def start(self, answer):
self.answer = answer
thread_refs = []
t1 = MyThread(70, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(2, 'Not finished')
t2.daemon = True
t2.start()
while True:
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self):
global results
return(results)
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
3 个回答
我对线程不太熟悉,但因为还没有人回答,我就试试吧。
在这里:你能不能在前面加两个if语句呢?
while True:
if t1.askFinished == 'Finished':
print("t1 Finished")
if t2.askFinished == 'Finished':
print("t2 Finished")
if t1.AskFinished == 'Finished' and t2.AskFinished == 'Finished': #If I break the loop after EACH site, Only the first to finish will be sent via GetResults to display1.py
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
补充:我尽量不改动你的代码... 说实话,它写得不是很好。(没有冒犯的意思)
更新:
好的,你想知道更多关于如何让 display1.py
打印结果的事情。如果你能解释一下为什么这很重要,那可能会影响你该怎么做,但这里有一个初步的思路:
# threads1.py
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.threads = {}
def start(self):
t1 = MyThread(4)
t1.daemon = True
t1.start()
self.threads[1] = t1
t2 = MyThread(1)
t2.daemon = True
t2.start()
self.threads[2] = t2
def is_alive(self, thread_id):
return self.threads[thread_id].is_alive()
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait):
Thread.__init__(self)
self.SleepWait = SleepWait
def run(self):
time.sleep(self.SleepWait)
然后……
# display1.py
from threads1 import *
manager = ThreadManager()
manager.start()
t1_state = t2_state = True
while manager.is_alive(1) or manager.is_alive(2):
time.sleep(1)
if manager.is_alive(1) != t1_state:
print("t1 finished")
t1_state = manager.is_alive(1)
if manager.is_alive(2) != t2_state:
print("t2 finished")
t2_state = manager.is_alive(2)
if not manager.is_alive(1) and not manager.is_alive(2):
print("Both Finished")
break
你最终应该考虑使用一个队列,正如 Crast 所建议的;但我们先专注于把这个搞对。
原始帖子:
这段代码有几个问题。
首先,你应该用 t1.is_alive()
来检查一个线程是否完成。没必要用 AskFinished
再重新实现一次。
其次,while True:
循环在 threads1.py 中以一种疯狂的速度运行,但实际上什么都没做,只是在等待你的线程结束。如果你不相信我,可以看看运行时的 CPU 使用率。你应该在里面加一个 time.sleep(1)
的语句。
第三,为什么要用全局变量来返回结果?这真的很奇怪。直接把结果存到 self 里就行了!
最后,为什么 display1.py
必须打印消息?为什么 thread1.py
不能做这个?
考虑到这四点,这里有一个更合理的 thread1.py:
from threading import Thread
import time
class ThreadManager:
def __init__(self):
self.results = None
def start(self, answer): # why is "answer" here?
self.answer = answer
thread_refs = []
t1 = MyThread(4, 'Not finished')
t1.daemon = True
t1.start()
t2 = MyThread(1, 'Not finished')
t2.daemon = True
t2.start()
t1_state = t2_state = True
while t1.is_alive() or t2.is_alive():
time.sleep(1)
if t1.is_alive() != t1_state:
print("t1 finished")
t1_state = t1.is_alive()
if t2.is_alive() != t2_state:
print("t2 finished")
t2_state = t2.is_alive()
if not t1.is_alive() and not t2.is_alive():
self.results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
def GetResults(self): # or you could just access results directly
return self.results
class MyThread(Thread):
def __init__(self, SleepWait, AskFinished):
Thread.__init__(self)
self.SleepWait = SleepWait
self.AskFinished = AskFinished
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
现在,这个代码仍然没有完全实现你想要的,因为你要求 display.py
来进行显示。要让这个工作,你需要把 while True
循环放到 display.py
中,并添加一个 ThreadManager.is_alive()
方法,这样 display.py
就可以用来检查线程是否还活着。如果你想知道怎么做,告诉我一声。
你这里所做的(在主线程中进入一个非常紧凑的检查循环)在很多编程语言中都是一种很简单的线程处理方式,尤其是在Python中,因为全局解释器锁(GIL)的竞争会让线程变得很慢。
更好的做法是使用queue.Queue来推送线程完成的信息。这样,主线程就可以在队列上等待,这样对CPU的消耗更少,同时也能让你知道(虽然顺序可能不一样)哪个线程完成了。
你需要做的修改有:
在模块 threads1.py
的顶部:
import queue
finished_queue = queue.Queue()
在你的 start() 函数中:
num_finished = 0
while True:
info = finished_queue.get()
num_finished += 1
if info is t1:
print("t1 finished")
else:
print("t2 finished")
if num_finished == 2:
global results
results = [t1.AskFinished, t2.AskFinished]
print("Both Finished")
break
最后在 run() 函数中:
def run(self):
time.sleep(self.SleepWait)
self.AskFinished = 'Finished'
finished_queue.put(self)
我还会做一些更基本的修改,实际上是把结果推送到队列中,然后再从队列中取出结果,省去在 GetResults 之前的那一步。此外,如果 GetResults 必须保留,我会通过 self
的一个字段来传递它们,比如 self.results = [t1.AskFinished, t2.AskFinished]
。