如何使用 threading 模块暂停和恢复线程?

Posted

技术标签:

【中文标题】如何使用 threading 模块暂停和恢复线程?【英文标题】:How to pause and resume a thread using the threading module? 【发布时间】:2011-03-16 19:06:12 【问题描述】:

我计划在线程中运行一个很长的进程,否则它将冻结我的 wxpython 应用程序中的 UI。

我正在使用:

threading.Thread(target=myLongProcess).start()

启动线程并且它可以工作,但我不知道如何暂停和恢复线程。我在 Python 文档中查找了上述方法,但找不到它们。

谁能建议我如何做到这一点?

【问题讨论】:

【参考方案1】:

我也做了一些速度测试,设置标志和采取行动的时间在慢速 2 处理器 Linux 机器上快得令人愉悦 0.00002 秒。

使用set()clear() 事件的线程暂停测试示例:

import threading
import time

# This function gets called by our thread.. so it basically becomes the thread init...                
def wait_for_event(e):
    while True:
        print('\tTHREAD: This is the thread speaking, we are Waiting for event to start..')
        event_is_set = e.wait()
        print('\tTHREAD:  WHOOOOOO HOOOO WE GOT A SIGNAL  : %s' % event_is_set)
        # or for Python >= 3.6
        # print(f'\tTHREAD:  WHOOOOOO HOOOO WE GOT A SIGNAL  : event_is_set')
        e.clear()

# Main code
e = threading.Event()
t = threading.Thread(name='pausable_thread', 
                     target=wait_for_event,
                     args=(e,))
t.start()

while True:
    print('MAIN LOOP: still in the main loop..')
    time.sleep(4)
    print('MAIN LOOP: I just set the flag..')
    e.set()
    print('MAIN LOOP: now Im gonna do some processing')
    time.sleep(4)
    print('MAIN LOOP:  .. some more processing im doing   yeahhhh')
    time.sleep(4)
    print('MAIN LOOP: ok ready, soon we will repeat the loop..')
    time.sleep(2)

【讨论】:

为了回答这个问题,您可能需要一个对象 thread_resume = threading.Event(),然后是 thread_resume.set()。在您的线程中,您可以在方便的时候检查 thread_resume.wait()。如果另一个线程调用thread_resume.clear(),该线程将暂停,并在另一个线程调用thread_resume.set()时恢复 这些是我见过的最激动人心的评论和输出字符串。 但是I just set the flag 应该在e.set() innit 之后。【参考方案2】:

我遇到了同样的问题。在线程循环中使用 time.sleep(1800) 来暂停线程执行更有效。

例如

MON, TUE, WED, THU, FRI, SAT, SUN = range(7) #Enumerate days of the week
Thread 1 : 
def run(self):
        while not self.exit:
            try:
                localtime = time.localtime(time.time())
                #Evaluate stock
                if localtime.tm_hour > 16 or localtime.tm_wday > FRI:
                    # do something
                    pass
                else:
                    print('Waiting to evaluate stocks...')
                    time.sleep(1800)
            except:
                print(traceback.format_exc())

Thread 2
def run(self):
    while not self.exit:
        try:
            localtime = time.localtime(time.time())
            if localtime.tm_hour >= 9 and localtime.tm_hour <= 16:
                # do something
                pass
            else:
                print('Waiting to update stocks indicators...')
                time.sleep(1800)
        except:
            print(traceback.format_exc())

【讨论】:

【参考方案3】:

多处理模块在 Windows 上运行良好。请参阅此处的文档(第一段结尾):

http://docs.python.org/library/multiprocessing.html

在 wxPython IRC 频道上,我们有几个人尝试了多处理,他们说它有效。不幸的是,我还没有看到有人写过一个很好的多处理和 wxPython 示例。

如果你(或这里的其他人)想出一些东西,请将其添加到 wxPython wiki 页面上的线程:http://wiki.wxpython.org/LongRunningTasks

您可能想查看该页面,因为它有几个使用线程和队列的有趣示例。

【讨论】:

【参考方案4】:

你可以看看Windows API for thread suspension。

据我所知,没有 POSIX/pthread 等价物。此外,我无法确定线程句柄/ID 是否为made available from Python。 Python 也存在潜在问题,因为它的调度是使用本机调度程序完成的,它不太可能期望线程挂起,特别是如果线程在持有 GIL 时挂起,以及其他可能性。

【讨论】:

【参考方案5】:

没有其他线程强制暂停线程的方法(就像其他线程杀死该线程一样)-目标线程必须通过偶尔检查适当的“标志”来合作(threading.Condition 可能是适用于暂停/取消暂停的情况)。

如果您使用的是 unix-y 平台(基本上除了 windows 之外的任何平台),您可以使用 multiprocessing 而不是 threading -- 那个 更强大,并且可以让您向“其他进程”发送信号; SIGSTOP 应该无条件地暂停一个进程,SIGCONT 继续它(如果你的进程需要在它暂停之前做一些事情,还要考虑 SIGTSTP 信号,其他进程可以捕捉到它来执行这种预暂停职责。(可能有一些方法可以在 Windows 上获得相同的效果,但我不了解它们(如果有的话)。

【讨论】:

不幸的是,我的目标是 Windows,所以我认为我不能使用多处理。我不希望一个线程暂停另一个线程,我只想向线程发送一条消息,告诉它自己暂停(或者这与告诉一个线程暂停另一个线程是一样的吗?),可以通过调整线程类? “发送消息”可以从字面上通过队列来完成,但是(正如我所说)对于这种带有条件的情况更实际。但是目标线程必须定期检查条件(或队列)才能接收“消息”:您无法“强制”它“接收消息”,您必须编码 以这种方式显式(可能带有循环)。其他线程无法强制使线程“接收针对它的消息”,以您似乎更喜欢的术语投射我的 A 的第一句话。顺便说一句,Windows 确实有一个 SuspendThread API(续) 对于特定于 Windows 的解决方案,请参阅 msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx -- 您可以使用 ctypes 或 sourceforge.net/projects/pywin32 直接从 Python 调用 Windows API。【参考方案6】:

您可以使用信号:http://docs.python.org/library/signal.html#signal.pause

为避免使用信号,您可以使用令牌传递系统。如果您想从主 UI 线程暂停它,您可能只需要使用 Queue.Queue 对象与其通信。

只需弹出一条消息,告诉线程休眠一段时间到队列中。

或者,您可以简单地将令牌从主 UI 线程连续推送到队列中。工作人员应该每 N 秒(0.2 或类似的时间)检查一次队列。当没有令牌出队时,工作线程将阻塞。当您希望它重新开始时,只需再次开始将令牌从主线程推送到队列中。

【讨论】:

不,你不能向线程发送信号——只能向进程发送信号!而signal.pause 仅表示调用进程休眠,直到收到信号——对 OP 的请求没有用处。请参阅我的答案以了解正确的方法。 @Alex - 感谢您的澄清。我的反应的信号部分是事后的想法..欢呼:)

以上是关于如何使用 threading 模块暂停和恢复线程?的主要内容,如果未能解决你的问题,请参考以下文章

暂停和恢复 Grand Central Dispatch 线程

在 C# 中暂停/恢复线程

threading模块

求助python多线程,执行到100多个停止了

暂停和恢复 std::thread 的正确方法

threading多线程总结