假设我有这个产生线程的类:
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()
函数中调用一个函数,但这不是我的目标。
答案 0 :(得分:44)
你无法直接做你想做的事。后台线程正在运行其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
现在,你可以这样做:
someClass = SomeClass()
someClass.start()
someClass.onThread(someClass.doSomething)
someClass.onThread(someClass.doSomethingElse)
someClass.onThread(someClass.doSomething)
如果你想稍微简化一下调用接口,可能会增加类中的代码,你可以像这样添加包装器方法:
def _doSomething(self):
# put the real code here
def doSomething(self):
self.onThread(self._doSomething)
但是,除非你的idle
方法有工作要做,否则你实际上只是在构建一个单线程线程池的等价物,并且有更简单的方法来做到这一点,而不是从头开始构建它。例如,使用PyPI的futures
模块(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)
或者,仅使用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()
池有一些其他优点,执行者甚至更多。例如,如果方法返回值,并且你想启动两个函数,然后等待结果,然后用前两个结果开始第三个怎么办?易:
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个主题的池,因此f1
和f2
可能会同时等待,f2
甚至可能首先返回,因此您可以保证启动{ {1}}一旦完成,就会立即完成。
这里有另一种可能性。你真的需要代码在该线程中运行,还是只需要它来修改线程所依赖的变量?如果是后者,只需同步访问变量即可。例如:
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()
依赖的变量之外,你还想从主线程中取出SomeClass
,这样你就可以做更重要的事情而不是等待它完成,你可以为doSomething
创建一个短命的额外线程:
doSomething
答案 1 :(得分:1)
查看concurrent.futures及其submit
方法,当您将线程池限制为一个工作线时,它会执行您想要的操作。