如何在正在运行的 Python 线程上调用函数 [英] How to call a function on a running Python thread

查看:95
本文介绍了如何在正在运行的 Python 线程上调用函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我想

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

我该怎么做?我知道我可以在 run() 函数中调用一个函数,但这不是我的目标.

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 函数,该函数永远循环,所以它不可能做任何其他事情.

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

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

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

Qt、.NET 或 Cocoa 等框架可以提供 runOnOtherThread 类型方法的原因是每个线程都运行一个事件循环",因此它们真正要做的就是发布一个事件.如果您将 run 方法重写为事件循环,您可以自己完成.例如:

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 run method into an event loop. For example:

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

现在,您可以这样做:

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)

<小时>

但是,除非您的 idle 方法有工作要做,否则您实际上只是在这里构建了一个单线程线程池,并且有比构建更简单的方法来做到这一点它从头开始.例如,使用 PyPI 的 futures 模块(Python 3 concurrent.futures 模块):


However, unless your idle method 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 futures module off PyPI (a backport of the Python 3 concurrent.futures module):

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)

或者,只使用标准库:

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

即使你后来切换到 4 个线程池,所以 f1f2 可能同时等待,f2 甚至可能先返回,您肯定会在它们都完成后立即开始 doSomethingElser,而且不会很快.

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

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

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

在这里的主线程上并没有什么神奇之处.如果,除了需要修改 SomeClass 所依赖的变量之外,您还想将 doSomething 踢出主线程,这样您就可以做比只是等待更重要的事情为了完成它,你可以创建一个短暂的额外线程来doSomething:

There's nothing magical about being on the main thread here. If, in addition to needing to modify variables that SomeClass depends on, you also wanted to just kick doSomething off 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 = SomeClass()
someClass.start()
somethingThread = threading.Thread(target=someClass.doSomething)
somethingThread.start()
doOtherImportantStuffWithSomethingIsHappening()
somethingThread.join()

这篇关于如何在正在运行的 Python 线程上调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆