等待Twisted和PB的活动

时间:2010-06-22 18:56:33

标签: python multithreading twisted

我有一个使用多个线程的python应用程序,我很好奇在python中等待某些东西的最佳方法,而不会烧掉cpu或锁定GIL。

我的应用程序使用twisted并且我生成一个线程来运行一个长操作,所以我不会踩到反应器线程。这个长操作也会使用twisted的deferToThread产生一些线程来做其他事情,而原始线程想要等待deferreds的结果。

我一直在做的是这个

while self._waiting:
    time.sleep( 0.01 )

这似乎破坏了扭曲的PB对象接收消息所以我认为睡眠锁定了GIL。下面的海报进一步调查显示它没有。

有更好的方法可以在不阻塞下面发布的反应器线程或python的情况下等待线程。

5 个答案:

答案 0 :(得分:13)

如果您已经在使用Twisted,则永远不需要像这样“等待”。

正如你所描述的那样:

  

我生成一个线程来运行一个长操作...这个长操作也会使用twisted的deferToThread生成一些线程......

这意味着您从“长操作”线程调用deferToThread,而不是从主线程(reactor.run()正在运行的线程)调用。正如Jean-Paul Calderone在评论中已经指出的那样,您只能 从主反应堆线程中调用Twisted API(例如deferToThread)。

您所看到的锁定是不遵守此规则的常见症状。它与GIL无关,而且与你将Twisted反应堆置于破碎状态这一事实有关。

根据您对程序的松散描述,我尝试编写一个示例程序,完全基于Twisted API执行您所说的内容,通过Twisted生成所有线程并从主反应器线程控制所有线程。

import time

from twisted.internet import reactor
from twisted.internet.defer import gatherResults
from twisted.internet.threads import deferToThread, blockingCallFromThread

def workReallyHard():
    "'Work' function, invoked in a thread."
    time.sleep(0.2)

def longOperation():
    for x in range(10):
        workReallyHard()
        blockingCallFromThread(reactor, startShortOperation, x)
    result = blockingCallFromThread(reactor, gatherResults, shortOperations)
    return 'hooray', result

def shortOperation(value):
    workReallyHard()
    return value * 100

shortOperations = []

def startShortOperation(value):
    def done(result):
        print 'Short operation complete!', result
        return result
    shortOperations.append(
        deferToThread(shortOperation, value).addCallback(done))

d = deferToThread(longOperation)
def allDone(result):
    print 'Long operation complete!', result
    reactor.stop()
d.addCallback(allDone)

reactor.run()

请注意,在反应堆停止的allDone点,您可以启动另一个“长时间操作”并让它重新开始此过程。

答案 1 :(得分:5)

你试过condition variables吗?它们像

一样使用
condition = Condition()

def consumer_in_thread_A():
    condition.acquire()
    try:
        while resource_not_yet_available:
            condition.wait()
        # Here, the resource is available and may be 
        # consumed
    finally:
        condition.release()

def produce_in_thread_B():
    # ... create resource, whatsoever
    condition.acquire()
    try:
        condition.notify_all()
    finally:
        condition.release()

条件变量充当锁(acquirerelease),但它们的主要目的是提供允许waitnotify的控制机制 - d或notify_all - d。

答案 2 :(得分:5)

  我最近发现了这个电话   time.sleep( X )将锁定GIL   整个时间X因此冻结   那个时候的所有python线程   周期。

您发现错误 - 这绝对是它是如何工作的。您发现此错误信息的来源是什么?

无论如何,那么你澄清(在评论中 - 更好地编辑你的Q!)你正在使用deferToThread并且你的问题是......:

  

嗯,是的,我将行动推迟到了   线程并给予扭曲回调。   但是父线程需要等待   对于整个子线程系列来说   完成之前它可以移动到新的   要生成的子线程集

因此,使用带有计数器的对象的方法作为回调 - 将其从0开始,每次延迟到线程时将其递增1并在回调方法中将其递减1。

当回调方法看到递减的计数器已经回到0时,它知道我们已经完成了等待“整个系列的子线程完成”,然后时间已经到了“转向新的”产生子线程的集合“,因此,仅在这种情况下,调用”生成一组新的子线程“函数或方法 - 就这么简单!

E.g。 (从错别字和c开始,因为这是未经测试的代码,只是为了给你这个想法)......:

class Waiter(object):

  def __init__(self, what_next, *a, **k):
    self.counter = 0
    self.what_next = what_next
    self.a = a
    self.k = k

  def one_more(self):
    self.counter += 1

  def do_wait(self, *dont_care):
    self.counter -= 1
    if self.counter == 0:
    self.what_next(*self.a, **self.k)


def spawn_one_thread(waiter, long_calculation, *a, **k):
  waiter.one_more()
  d = threads.deferToThread(long_calculation, *a, **k)
  d.addCallback(waiter.do_wait)

def spawn_all(waiter, list_of_lists_of_functions_args_and_kwds):
  if not list_of_lists_of_functions_args_and_kwds:
    return
  if waiter is None:
    waiter=Waiter(spawn_all, list_of_lists_of_functions_args_and_kwds)
  this_time = list_of_list_of_functions_args_and_kwds.pop(0)
  for f, a, k in this_time:
    spawn_one_thread(waiter, f, *a, **k)

def start_it_all(list_of_lists_of_functions_args_and_kwds):
  spawn_all(None, list_of_lists_of_functions_args_and_kwds)

答案 3 :(得分:2)

根据Python源代码,time.sleep()不包含GIL。

http://code.python.org/hg/trunk/file/98e56689c59c/Modules/timemodule.c#l920

请注意使用Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS,如下所示:

http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

答案 4 :(得分:1)

threading模块允许您生成一个线程,然后由Thread对象表示。该对象具有join方法,您可以使用该方法等待子线程完成。

请参阅http://docs.python.org/library/threading.html#module-threading