11

我有一个生成多个线程的 python 程序。这些线程持续 2 秒到 30 秒之间的任何时间。在主线程中,我想跟踪每个线程何时完成并打印一条消息。如果我只是按顺序 .join() 所有线程并且第一个线程持续 30 秒并且其他线程更快地完成,我将无法更快地打印消息 - 所有消息将在 30 秒后打印。

基本上我想阻塞直到任何线程完成。一旦线程完成,打印一条关于它的消息,如果任何其他线程仍然存在,则返回阻塞。如果所有线程都完成,则退出程序。

我能想到的一种方法是让一个队列传递给所有线程并阻塞 queue.get()。每当从队列接收到消息时,打印它,使用 threading.active_count() 检查是否有任何其他线程处于活动状态,如果是,则返回到 queue.get() 上的阻塞。这会起作用,但这里所有线程都需要遵循在终止之前向队列发送消息的规则。

我想知道这是实现这种行为的传统方式还是有其他/更好的方式?

4

7 回答 7

7

这是@detly 答案的一个变体,可让您从主线程中指定消息,而不是从目标函数中打印它们。这将创建一个包装函数,该函数调用您的目标,然后在终止之前打印一条消息。您可以修改它以在每个线程完成后执行任何类型的标准清理。

#!/usr/bin/python

import threading
import time

def target1():
    time.sleep(0.1)
    print "target1 running"
    time.sleep(4)

def target2():
    time.sleep(0.1)
    print "target2 running"
    time.sleep(2)

def launch_thread_with_message(target, message, args=[], kwargs={}):
    def target_with_msg(*args, **kwargs):
        target(*args, **kwargs)
        print message
    thread = threading.Thread(target=target_with_msg, args=args, kwargs=kwargs)
    thread.start()
    return thread

if __name__ == '__main__':
    thread1 = launch_thread_with_message(target1, "finished target1")
    thread2 = launch_thread_with_message(target2, "finished target2")

    print "main: launched all threads"

    thread1.join()
    thread2.join()

    print "main: finished all threads"
于 2015-09-04T19:00:32.037 回答
6

需要使用Thread.is_alive()调用检查线程。

于 2012-05-03T23:33:28.460 回答
4

为什么不让线程自己打印完成消息,或者在完成后调用其他完成回调?

您可以只join从主程序中提取这些线程,因此您会看到一堆完成消息,并且您的程序将根据需要在它们全部完成后终止。

这是一个快速简单的演示:

#!/usr/bin/python

import threading
import time

def really_simple_callback(message):
    """
    This is a really simple callback. `sys.stdout` already has a lock built-in,
    so this is fine to do.
    """    
    print message

def threaded_target(sleeptime, callback):
    """
    Target for the threads: sleep and call back with completion message.
    """
    time.sleep(sleeptime)
    callback("%s completed!" % threading.current_thread())

if __name__ == '__main__':
    # Keep track of the threads we create
    threads = []

    # callback_when_done is effectively a function
    callback_when_done = really_simple_callback

    for idx in xrange(0, 10):
        threads.append(
            threading.Thread(
                target=threaded_target,
                name="Thread #%d" % idx,
                args=(10 - idx, callback_when_done)
            )
        )

    [t.start() for t in threads]
    [t.join() for t in threads]

    # Note that thread #0 runs for the longest, but we'll see its message first!
于 2012-05-04T00:32:06.960 回答
2

我建议的是这样的循环

while len(threadSet) > 0:
    time.sleep(1)
    for thread in theadSet:
        if not thread.isAlive()
            print "Thread "+thread.getName()+" terminated"
            threadSet.remove(thread)

有 1 秒的休眠,所以线程终止和打印消息之间会有一点延迟。如果您可以忍受这种延迟,那么我认为这比您在问题中提出的解决方案更简单。

于 2012-05-04T00:22:10.290 回答
1

您可以让线程将其结果推送到threading.Queue. 让另一个线程在此队列上等待,并在出现新项目时立即打印消息。

于 2012-05-03T23:29:58.277 回答
0

我不确定我是否看到使用的问题:threading.activeCount()

跟踪仍处于活动状态的线程数?

即使您在开始之前不知道要启动多少线程,它似乎也很容易跟踪。我通常通过列表理解生成线程集合,然后使用 activeCount 与列表大小进行简单比较可以告诉你有多少已经完成。

见这里:http ://docs.python.org/library/threading.html

或者,一旦你有了你的线程对象,你就可以在线程对象中使用 .isAlive 方法来检查。

我刚刚通过将其放入我拥有的多线程程序中进行检查,它看起来不错:

for thread in threadlist:
        print(thread.isAlive())

当线程打开和关闭时,给我一个 True/False 列表。所以你应该能够做到这一点并检查任何 False 以查看是否有任何线程完成。

于 2012-05-03T23:29:51.157 回答
0

由于我在应用程序中使用的线程的性质,我使用了一种稍微不同的技术。为了说明,这是我编写的一个测试带程序的片段,用于为我的线程类搭建一个屏障类:

   while threads:
        finished = set(threads) - set(threading.enumerate())
        while finished:
            ttt = finished.pop()
            threads.remove(ttt)
        time.sleep(0.5)

为什么我要这样做?在我的生产代码中,我有时间限制,所以第一行实际上是“while threads and time.time() < cutoff_time”。如果我达到截止时间,我就有代码告诉线程关闭。

于 2016-09-08T14:55:18.810 回答