Python 3,多线程 - 当每个线程完成时将数据传入主模块

0 投票
3 回答
2444 浏览
提问于 2025-04-16 12:09

下面的代码运行了两个线程(多线程),每个线程的延迟时间不同,这样每个线程完成的时间也就不一样。

一旦两个线程都完成,模块 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 个回答

0

我对线程不太熟悉,但因为还没有人回答,我就试试吧。

在这里:你能不能在前面加两个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

补充:我尽量不改动你的代码... 说实话,它写得不是很好。(没有冒犯的意思)

1

更新

好的,你想知道更多关于如何让 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 就可以用来检查线程是否还活着。如果你想知道怎么做,告诉我一声。

2

你这里所做的(在主线程中进入一个非常紧凑的检查循环)在很多编程语言中都是一种很简单的线程处理方式,尤其是在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]

撰写回答