我正在尝试自动执行gui背后的任务。我的gui有多个按钮,可以启动某些功能,一个按钮可以停止当前正在运行的任何功能。当一个功能运行时,除“取消”按钮以外的所有按钮都显示为灰色。我需要额外的线程,以便在冗长的函数运行时我的gui仍在响应。
现在我想实现一个装饰器来装饰这些功能。这个装饰器应该在一个单独的线程中运行该函数。当用户按下取消按钮时,该额外线程中的功能应该接收某种停止信号,装饰功能可以用来停止当前任务并退出。
我知道可以为每个函数实现一个类型为threading.Thread,并将当前函数作为“def run(self):”插入,但它似乎不是一个优雅的解决方案。
有办法吗?对我而言,这似乎是一个常见的问题,但我没有找到任何解决方案,除了将函数编写为类并将它们作为单独的线程运行。
编辑1:
让我补充一些例子。现在我的功能看起来像这样:
def function1:
function_code
但如果我创建类,它将如下所示:
class class1(threading.Thread):
stopper = None
def __init__(self):
init_code
def run(self):
function_code
def function1:
t = class1()
t.stopper = some_stop_condition
t.run()
第二个代码要长得多,每个按钮都需要一个类和一个函数。它看起来更加复杂,我希望这是不必要的。
我错了还是我做错了什么?
编辑2:
我的新代码,在salomonderossi的典范之后:
def run_async(func):
@functools.wraps(func)
def async_func(*args, **kwargs):
queue = Queue.Queue()
t = threading.Thread(target=func, args=(queue,) + args, kwargs=kwargs)
t.start()
return queue, t
return async_func
# @layout_decorators.runInSeparateThread()
@run_async
def test2(myQueue):
print "executing test2"
import time
for k in range(6):
print k
try:
myQueue.get(False)
except Queue.Empty:
print "cancelled"
return
time.sleep(1)
def test22():
print "executing test22"
global globalQueue
globalQueue, t = test2()
if __name__ == "__main__":
import time
print "\n\n"
test22()
time.sleep(2)
globalQueue.put("stop")
但它在第一次机会时就停止了线程。即使我删除了最后一行(我认为它会停止线程),我的输出是
executing test22
executing test2
0
cancelled
答案 0 :(得分:3)
装饰者需要创建一种与线程进行通信的方法。在此示例中,应作为线程运行的每个函数必须首先具有queue
参数。此queue
用于与线程通信。在此示例中,可以将任何内容放入队列以停止线程,因为该函数仅检查是否可以将值放出队列。
使用queue.get(False)
函数尝试从队列中获取元素(无需等待)。如果没有元素(队列为空),则引发异常。否则,队列中有一些东西,并告诉线程退出。
要告诉线程退出,必须在线程队列中放入一些东西。这是通过queue.put("stop")
完成的。在这种情况下,论证并不重要。
这意味着,如果队列中存在某些内容并对其作出反应,则必须检查规则(在这种情况下只是停止处理)。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from threading import Thread
from functools import wraps
from time import sleep
try:
import queue as Queue
except ImportError:
import Queue as Queue
def run_async(func):
@wraps(func)
def async_func(*args, **kwargs):
queue = Queue.Queue()
t = Thread(target=func, args=(queue,) + args, kwargs=kwargs)
t.start()
return queue, t
return async_func
@run_async
def do_something_else(queue):
while True:
sleep(1)
print("doing something else")
# check if something in the queue and return if so
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
@run_async
def print_somedata(queue):
print('starting print_somedata')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('print_somedata: 2 sec passed')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('print_somedata: another 2 sec passed')
sleep(2)
try:
queue.get(False)
except Queue.Empty:
pass
else:
print("Told to quit")
return
print('finished print_somedata')
def test():
threads = list()
# at this moment the thread is created and starts immediately
threads.append(print_somedata())
print('back in main')
# at this moment the thread is created and starts immediately
threads.append(print_somedata())
print('back in main')
# at this moment the hread is created and starts immediately
threads.append(do_something_else())
print('back in main')
# at this moment the hread is created and starts immediately
threads.append(do_something_else())
print('back in main')
print("Wait a bit in the main")
sleep(1) # uncomment the wait here to stop the threads very fast ;)
# you don't have to wait explicitly, as the threads are already
# running. This is just an example to show how to interact with
# the threads
for queue, t in threads:
print("Tell thread to stop: %s", t)
queue.put('stop')
#t.join()
if __name__ == '__main__':
test()