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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19033818/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 12:36:57  来源:igfitidea点击:

How to call a function on a running Python thread

pythonpython-2.7

提问by Chris F

Say I have this class that spawns a thread:

假设我有一个生成线程的类:

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

I want to

我想要

someClass = SomeClass()
someClass.start()
someClass.doSomething()
someClass.doSomethingElse()
someClass.doSomething()

How can I do this? I know I can call a function inside the run()function, but that's not what I am aiming for.

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

采纳答案by abarnert

You can't directly do what you want. The background thread is running its runfunction, which just loops forever, so it can't possibly do anything else.

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

You can, of course, call the class's methods on your own thread, but that presumably isn't what you want here.

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



The reason frameworks like Qt, .NET, or Cocoa can offer runOnOtherThread-type methods is that each thread runs an "event loop", so all they're really doing is posting an event. You can do that yourself, if you rewrite the runmethod into an event loop. For example:

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

Now, you can do this:

现在,您可以这样做:

someClass = SomeClass()
someClass.start()
someClass.onThread(someClass.doSomething)
someClass.onThread(someClass.doSomethingElse)
someClass.onThread(someClass.doSomething)

If you want to simplify the calling interface a bit, at the cost of more code in the class, you can add wrapper methods like this:

如果你想稍微简化调用接口,以类中更多的代码为代价,你可以添加这样的包装方法:

    def _doSomething(self):
        # put the real code here
    def doSomething(self):
        self.onThread(self._doSomething)


However, unless your idlemethod has work to do, you're really just building the equivalent of a single-thread thread pool here, and there are much easier ways to do this than to build it from scratch. For example, using the futuresmodule off PyPI (a backport of the Python 3 concurrent.futuresmodule):

但是,除非您的idle方法有工作要做,否则您实际上只是在这里构建了一个单线程线程池,并且有比从头构建它更简单的方法。例如,使用futuresPyPI 外的模块(Python 3concurrent.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)

Or, with just the stdlib:

或者,只使用 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()

Pools have some other advantages, and executors even more. For example, what if the methods returned values, and you want to kick off two functions, then wait for the results, then kick off a third with the results of the first two? Easy:

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

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

Even if you later switch to a pool of 4 threads, so f1and f2may be waiting concurrently and f2may even return first, you're guaranteed to kick off doSomethingElseras soon as both of them are finished, and no sooner.

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



There's another possibility here. Do you really need the code to run in that thread, or do you just need it to modify variables that thread depends on? If it's the latter, just synchronize access to the variables. For example:

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

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

There's nothing magical about being on the main thread here. If, in addition to needing to modify variables that SomeClassdepends on, you also wanted to just kick doSomethingoff the main thread so you can do more important things than just waiting around for it to finish, you can create a short-lived extra thread just to doSomething:

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

someClass = SomeClass()
someClass.start()
somethingThread = threading.Thread(target=someClass.doSomething)
somethingThread.start()
doOtherImportantStuffWithSomethingIsHappening()
somethingThread.join()

回答by jhermann

Look into concurrent.futuresand its submitmethod, which does what you want when you limit the thread pool to one worker.

查看concurrent.futures及其submit方法,当您将线程池限制为一个工作人员时,它会执行您想要的操作。