【发布时间】:2017-06-14 18:04:47
【问题描述】:
我需要暂停和恢复线程,它不断地执行一些任务。执行在调用start() 时开始,不应中断,必须从调用pause() 时继续执行。
我该怎么做?
【问题讨论】:
-
真正的问题是“如何跟踪线程中的数据和步骤?”
标签: python multithreading
我需要暂停和恢复线程,它不断地执行一些任务。执行在调用start() 时开始,不应中断,必须从调用pause() 时继续执行。
我该怎么做?
【问题讨论】:
标签: python multithreading
请记住,在 Python 中使用线程不会授予您并行处理,除非是 IO 阻塞操作。有关这方面的更多信息,请查看 this 和 this
您不能在 Python 中任意暂停线程(在进一步阅读之前请记住这一点)。我不确定您是否有办法在操作系统级别执行此操作(例如,通过使用纯 C)。您可以做的是让线程在您事先考虑的特定点暂停。我举个例子:
class MyThread(threading.Thread):
def __init__(self, *args, **kwargs):
super(MyThread, self).__init__(*args, **kwargs)
self._event = threading.Event()
def run(self):
while True:
self.foo() # please, implement this.
self._event.wait()
self.bar() # please, implement this.
self._event.wait()
self.baz() # please, implement this.
self._event.wait()
def pause(self):
self._event.clear()
def resume(self):
self._event.set()
这种方法可行,但是:
self._event.wait())。编辑我没有测试过这个,但如果你需要多个这样的线程,也许不需要太多子类化就可以工作:
class MyPausableThread(threading.Thread):
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
self._event = threading.Event()
if target:
args = (self,) + args
super(MyPausableThread, self).__init__(group, target, name, args, kwargs)
def pause(self):
self._event.clear()
def resume(self):
self._event.set()
def _wait_if_paused(self):
self._event.wait()
这应该允许您通过调用MyPausableThread(target=myfunc).start() 创建一个无需更多子类化的自定义线程,并且您的可调用对象的第一个参数将接收线程对象,当您需要暂停检查时可以从中调用self._wait_if_paused()。
或者甚至更好,如果你想隔离目标访问线程对象:
class MyPausableThread(threading.Thread):
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
self._event = threading.Event()
if target:
args = ((lambda: self._event.wait()),) + args
super(MyPausableThread, self).__init__(group, target, name, args, kwargs)
def pause(self):
self._event.clear()
def resume(self):
self._event.set()
您的目标可调用对象将在第一个参数中接收一个可以像这样调用的函数:pause_checker()(前提是目标可调用对象中的第一个参数名为 pause_checker)。
【讨论】:
asyncio 在 Python 3.4 之前的版本中不存在,它本质上以更用户友好的方式使用相同的线程接口(具有可接受的开销),而 Tornado 是第三个第三方库 - 不需要第三方库来有效地使用 I/O 等语言的基本功能。此外,您绝对可以通过阻止 GIL 进行上下文切换来暂停线程。
您可以通过附加一个导致所有其他线程等待信号的跟踪函数来做到这一点:
import sys
import threading
import contextlib
# needed to enable tracing
if not sys.gettrace():
sys.settrace(lambda *args: None)
def _thread_frames(thread):
for thread_id, frame in sys._current_frames().items():
if thread_id == thread.ident:
break
else:
raise ValueError("No thread found")
# walk up to the root
while frame:
yield frame
frame = frame.f_back
@contextlib.contextmanager
def thread_paused(thread):
""" Context manager that pauses a thread for its duration """
# signal for the thread to wait on
e = threading.Event()
for frame in _thread_frames(thread):
# attach a new temporary trace handler that pauses the thread
def new(frame, event, arg, old = frame.f_trace):
e.wait()
# call the old one, to keep debuggers working
if old is not None:
return old(frame, event, arg)
frame.f_trace = new
try:
yield
finally:
# wake the other thread
e.set()
你可以用作:
import time
def run_after_delay(func, delay):
""" Simple helper spawning a thread that runs a function in the future """
def wrapped():
time.sleep(delay)
func()
threading.Thread(target=wrapped).start()
main_thread = threading.current_thread()
def interrupt():
with thread_paused(main_thread):
print("interrupting")
time.sleep(2)
print("done")
run_after_delay(interrupt, 1)
start = time.time()
def actual_time(): return time.time() - start
print("{:.1f} == {:.1f}".format(0.0, actual_time()))
time.sleep(0.5)
print("{:.1f} == {:.1f}".format(0.5, actual_time()))
time.sleep(2)
print("{:.1f} != {:.1f}".format(2.5, actual_time()))
给予
0.0 0.0
0.5 0.5
interrupting
done
2.5 3.0
注意中断如何导致主线程上的睡眠等待更长的时间
【讨论】:
您可以使用 psutil 库中的 Process 类来执行此操作。
例子:
>>> import psutil
>>> pid = 7012
>>> p = psutil.Process(pid)
>>> p.suspend()
>>> p.resume()
看到这个答案:https://stackoverflow.com/a/14053933
编辑:此方法将暂停整个进程,而不仅仅是一个线程。 (我不删除这个答案,所以其他人可以知道这个方法行不通。)
【讨论】:
while(int(any) < 2000):
sleep(20)
print(waiting any...)
【讨论】: