额外线程的装饰器

时间:2016-05-13 07:34:31

标签: python multithreading decorator

我正在尝试自动执行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

1 个答案:

答案 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()