用非阻塞方式确定线程是否完成?

6

I have the following code:

import threading
import time

class TestWorker (threading.Thread):
    def __init__(self, threadID, name):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name

    def run(self):
        print "Starting " + self.name
        time.sleep(20)
        print "Exiting " + self.name
        # how do I let the calling thread know it's done?

class TestMain:
    def __init__(self):
        pass

    def do_work(self):
        thread = TestWorker(1, "Thread-1")
        thread.start()

    def do_something_else(self):
        print "Something else"

    def on_work_done(self):
        print "work done"

如何让主线程知道TestWorker已经完成(调用on_work_done()),而不会像thread.join()一样阻塞对do_something_else()的调用?


4
thread.isAlive()。或者您是在寻找基于事件的解决方案? - Aran-Fey
这难道不意味着轮询线程,从而仍然阻塞主线程吗? - Douwe
1
所以你的问题实际上是“如何在线程完成后调用函数”,而不是“如何确定线程是否已经完成”?在这种情况下,您需要触发某种事件或者只需在TestWorker.run中调用TestMain.on_work_done() - Aran-Fey
1
如果我使用回调函数,工作线程不就会在执行on_work_done时,无法关闭(因为on_work_done本身可能需要一点时间)。基于事件的方式听起来不错,但我还没有找到一个与我在Python方面有限的经验相匹配的好资源。 - Douwe
如果你经验有限,为什么要折腾线程?只是好奇吗…… - Bruce David Wilner
1
经验有限,特别在Python方面。这个应用程序需要一个非阻塞的解决方案,而我就是负责提出解决方案的人。要回答为什么选择Python和为什么选择我这些问题需要一段时间。 - Douwe
3个回答

8
你可以为线程实例提供一个可选的回调函数,在线程结束时调用。
请注意,我添加了一个锁来防止并发打印(这会造成阻塞)。
print_lock = threading.Lock()  # Prevent threads from printing at same time.

class TestWorker(threading.Thread):
    def __init__(self, threadID, name, callback=lambda: None):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.callback = callback

    def run(self):
        with print_lock:
            print("Starting " + self.name)
        time.sleep(3)
        with print_lock:
            print("Exiting " + self.name)
        self.callback()

class TestMain:
    def __init__(self):
        self.work_done = False

    def do_work(self):
        thread = TestWorker(1, "Thread-1", self.on_work_done)
        thread.start()

    def do_something_else(self):
        with print_lock:
            print("Something else")

    def on_work_done(self):
        with print_lock:
            print("work done")
        self.work_done = True

main = TestMain()
main.do_work()
while not main.work_done:
    main.do_something_else()
    time.sleep(.5)  # do other stuff...

print('Done')

输出:

Starting Thread-1
Something else
Something else
Something else
Something else
Something else
Something else
Exiting Thread-1
work done
Done

1
运作得非常好,但我想知道如果在回调函数中启动另一个线程(在实际应用程序中可能会发生),那么该线程是MainThread的子线程还是TestWorker的子线程?(这是否重要?) - Douwe
我不确定,但我认为这并不重要,除非涉及到它是否是守护线程(其初始值继承自创建线程)。 - martineau
我会的,不用担心 :) 感谢您的时间和回答! - Douwe

1
import queue
import threading

class SThread(threading.Thread, queue.Queue):
    def __init__(self, queue_out: object):
        threading.Thread.__init__(self)
        queue.Queue.__init__(self)
        self.queue_out = queue_out
        self.setDaemon(True)
        self.start()

    def run(self):
        print('Thread start')
        while True:
            cmd = self.get()
            if cmd is None:
                break  # exit thread
            self.queue_out.put(cmd['target'](*cmd.get('args', ())), **cmd.get('kwargs', {}))
            self.task_done()
        print('Thread stop')

def testFn(a):
    print('+ %s' % a)
    return a

if __name__ == '__main__':
    print('main 1')
    # init
    queue_out = queue.Queue()
    thread = SThread(queue_out)

    # in
    for a in range(5): thread.put(dict(target=testFn, args=(a,)))

    thread.put(None)
    print('main 2')
    # out
    while True:
        try:
            print('- %s' % queue_out.get(timeout=3))
        except queue.Empty:
            break
    print('main 3')

输出:

main 1
Thread start
main 2
+ 0
+ 1
+ 2
+ 3
+ 4
Thread stop
- 0
- 1
- 2
- 3
- 4
main 3

0
import threading
dt = {}
threading.Thread(target=dt.update, kwargs=dict(out=123)).start()

while 'out' not in dt:
    print('out' in dt)
print(dt)

除非我轮询或使用超时来读取它的值,否则dt = {}。 - Douwe
while循环阻塞了主线程,并在工作线程运行期间不受检查地运行,这不是好的做法。 - Douwe
使用以下代码:queue.Queue() - queue.task_done() 在TestWorker中进行队列的输入和输出。 - vadim vaduxa
请更新你的回答。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接