如何在正在运行的Python线程上调用函数

Chr*_*s F 21 python python-2.7

假设我有这个产生线程的类:

import threading
class SomeClass(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while True:
            pass

    def doSomething(self):
        pass

    def doSomethingElse(self):
        pass
Run Code Online (Sandbox Code Playgroud)

我想要

someClass = SomeClass()
someClass.start()
someClass.doSomething()
someClass.doSomethingElse()
someClass.doSomething()
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?我知道我可以在函数内部调用一个函数run(),但这不是我的目标.

aba*_*ert 47

你不能直接做你想做的事.后台线程正在运行它的run函数,它只是永远循环,所以它不可能做任何其他事情.

当然,您可以在自己的线程上调用类的方法,但这可能不是您想要的.


像Qt,.NET或Cocoa这样的框架可以提供runOnOtherThread类型方法的原因是每个线程都运行一个"事件循环",所以他们真正做的就是发布一个事件.如果将run方法重写为事件循环,则可以自己执行此操作.例如:

import queue
import threading

class SomeClass(threading.Thread):
    def __init__(self, q, loop_time = 1.0/60):
        self.q = q
        self.timeout = loop_time
        super(SomeClass, self).__init__()

    def onThread(self, function, *args, **kwargs):
        self.q.put((function, args, kwargs))

    def run(self):
        while True:
            try:
                function, args, kwargs = self.q.get(timeout=self.timeout)
                function(*args, **kwargs)
            except queue.Empty:
                self.idle()

    def idle(self):
        # put the code you would have put in the `run` loop here 

    def doSomething(self):
        pass

    def doSomethingElse(self):
        pass
Run Code Online (Sandbox Code Playgroud)

现在,你可以这样做:

someClass = SomeClass()
someClass.start()
someClass.onThread(someClass.doSomething)
someClass.onThread(someClass.doSomethingElse)
someClass.onThread(someClass.doSomething)
Run Code Online (Sandbox Code Playgroud)

如果你想稍微简化一下调用接口,可能会增加类中的代码,你可以像这样添加包装器方法:

    def _doSomething(self):
        # put the real code here
    def doSomething(self):
        self.onThread(self._doSomething)
Run Code Online (Sandbox Code Playgroud)

但是,除非你的idle方法有工作要做,否则你实际上只是在构建一个单线程线程池的等价物,并且有更简单的方法来实现这一点,而不是从头开始构建它.例如,使用futuresPyPI模块(Python 3 concurrent.futures模块的后端):

import futures

class SomeClass(object):
    def doSomething(self):
        pass
    def doSomethingElse(self):
        pass

someClass = SomeClass()
with futures.ThreadPoolExecutor(1) as executor:
    executor.submit(someClass.doSomething)
    executor.submit(someClass.doSomethingElse)
    executor.submit(someClass.doSomething)
Run Code Online (Sandbox Code Playgroud)

或者,只使用stdlib:

from multiprocessing import dummy as multithreading

class SomeClass(object):
    def doSomething(self):
        pass
    def doSomethingElse(self):
        pass

someClass = SomeClass()
pool = multithreading.Pool(1)
pool.apply(someClass.doSomething)
pool.apply(someClass.doSomethingElse)
pool.apply(someClass.doSomething)
pool.close()
pool.join()
Run Code Online (Sandbox Code Playgroud)

池有一些其他优点,执行者甚至更多.例如,如果方法返回值,并且你想启动两个函数,然后等待结果,然后用前两个结果开始第三个怎么办?简单:

with futures.ThreadPoolExecutor(1) as executor:
    f1 = executor.submit(someClass.doSomething)
    f2 = executor.submit(someClass.doSomethingElse)
    futures.wait((f1, f2))
    f3 = executor.submit(someClass.doSomethingElser, f1.result(), f2.result())
    result = f3.result()
Run Code Online (Sandbox Code Playgroud)

即使您稍后切换到4个线程池,所以f1f2可以同时等待,f2甚至可以先返回,保证能让你揭开序幕doSomethingElser,只要他们两个都完了,没有更早.


这里有另一种可能性.你真的需要代码在该线程中运行,还是只需要它来修改线程所依赖的变量?如果是后者,只需同步访问变量即可.例如:

class SomeClass(threading.Thread):
    def __init__(self):
        self.things_lock = threading.Lock()
        self.things = []
        while True:
            with self.lock:
                things = self.things[:]
            for thing in things:
                # pass
    def doSomething(self):
        with self.lock:
            self.things.append(0)

someClass = SomeClass()
someClass.start()
someClass.doSomething()
Run Code Online (Sandbox Code Playgroud)

在这里主线程没有什么神奇之处.如果,除了需要修改SomeClass依赖的变量之外,你还想启动doSomething主线程以便你可以做更重要的事情,而不是等待它完成,你可以创建一个短暂的额外线程,只是为了doSomething:

someClass = SomeClass()
someClass.start()
somethingThread = threading.Thread(target=someClass.doSomething)
somethingThread.start()
doOtherImportantStuffWithSomethingIsHappening()
somethingThread.join()
Run Code Online (Sandbox Code Playgroud)

  • Waaaay迟到了这个派对,但这正是我所寻找的信息!谢谢3年前! (3认同)
  • 是的,我希望该对象在线程上运行。你的队列想法奏效了。 (2认同)