在Python线程中,我如何跟踪线程的完成?

时间:2012-05-03 23:18:35

标签: python multithreading

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

基本上我想阻止,直到任何线程完成。一旦线程完成,就打印一条关于它的消息,如果任何其他线程仍处于活动状态,则返回阻塞状态。如果所有线程都已完成,则退出程序。

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

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

7 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分: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!

答案 2 :(得分:3)

这是@ 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"

答案 3 :(得分: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秒,因此线程终止和正在打印的消息之间会有轻微的延迟。如果你能忍受这种延迟,那么我认为这是一个比你提出的问题更简单的解决方案。

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

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

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

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

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

我只是通过把它扔进我的多线程程序来检查它看起来很好:

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

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

答案 6 :(得分:0)

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

   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”。如果我到达截止点,那么我会有代码告诉线程关闭。