有没有办法干净地退出处理来自(永无止境)生成器的数据的线程?

时间:2010-10-01 19:27:26

标签: python

问题在于:我有一个线程,它从发生器运行for循环读取,对该数据进行一些处理等。生成器总是有数据进入,所以没有StopIteration异常它提出来了。我想从主线程(干净地)停止这个线程(即退出for循环,它正在处理来自生成器的数据)。下面是上述场景的一个例子,结果正确,但我将在下面描述的有限意义中描述:

import threading
import time
import random

def add():
    r = random.Random()
    i = 0
    while True:
        sleep_time = r.randint(0, 3)
        time.sleep(sleep_time)
        yield i
        i = i + 1

class Test(object):

    def __init__(self):
        self.func = add
        self.stopped = False

    def stop(self):
        self.stopped = True

    def run(self):
        self.generator = self.func()
        for x in self.generator:
            print x
            if self.stopped is True:
                break
        print 'DONE'


tester = Test()
thread = threading.Thread(target=tester.run)
thread.daemon = True
thread.start()
time.sleep(10)
print 'Stopping thread'
tester.stop()
print 'Complete, but should stop immediately!'

现在,虽然这在上面的例子中起作用(显然上面并没有阻止self.stopped上的竞争条件,但这不是手头的问题,所以我把代码遗漏了),我遇到的问题是我的实际代码中的生成器并不总是立即拥有数据,因此在设置self.stopped和实际执行break语句之间可能会有很长的暂停。所以,我的问题的主旨是我希望能够尽快干净地退出for循环,而不是在能够退出之前等待来自生成器的数据,显然上面的解决方案没有那样做。

有希望吗?这是一个很好的问题,可能没有干净的解决方案,但任何帮助都会非常感激。

编辑:为了澄清,在我的实际应用程序中,我有一个生成器(让我们将其表示为 G ),它从内核驱动程序中获取数据。此数据将被发送到服务器,但是当套接字尝试连接到服务器(可能并不总是正在运行)时,我想处理来自驱动程序的数据(一旦连接,此处理不会发生)。所以当主线程尝试连接到服务器时,我启动了一个线程来从 G 中获取数据(并处理它)。连接后,理想情况下应该发生以下情况:

我暂停执行 G ,退出线程,并将相同的G 实例传递给另一个将数据直接发送到服务器的函数。

从下面的答案/评论中,我相信如果不破坏 G ,这是不可能的,因为没有办法干净地暂停当前正在执行的生成器。

很抱歉这个混乱。

4 个答案:

答案 0 :(得分:0)

您需要self:generator具有超时功能。从概念上讲

wait(1 sec);

而不仅仅是

wait();

我不知道是否可能(向我们展示您的发电机代码)。例如,如果您正在读取管道或套接字,请不要编码

giveMeSomeBytes( buffer);  // wait indefinately

giveMeSomeBytesOrTimeout( buffer, howLongToWait); // wait for a while and 
                                                  // then go see if we should dies

答案 1 :(得分:0)

听起来你真正想要的是一个协程,而不是一个发电机。看看大卫比兹利令人费解的A Curious Course on Coroutines and Concurrency,虽然提供的信息超出了你的要求,但有些信息可以让你清楚地知道你想要做什么。

答案 2 :(得分:0)

难道你不能'关闭'发电机吗?做点什么

def stop(self):
        self.generator.close()

def run(self):
        self.generator = self.func()
        try:
          for x in self.generator:
              print x
              time.sleep(1)
        except GeneratorExit:
          pass
        print 'DONE'

答案 3 :(得分:0)

首先,发电机可能是红鲱鱼;不要担心他们。

在Python中解决此类生产者 - 消费者问题的规范方法是使用内置的queue模块。它充当中介,允许您的生产者线程继续从内核中抓取/处理数据到队列中,并且您的消费者线程将队列数据发送到服务器,而不会相应的阻塞I / O调用相互干扰。

这是基本想法的草图,没有填写详细信息:

from queue import Queue

class Application(object):

    def __init__(self):
        self.q = Queue()
        self.running = False

    # From kernel to queue
    def produce(self):
        while self.running:
            data = read_from_kernel()
            self.q.put(data)

    # From queue to server
    def consume(self):
        while self.running:
            data = self.q.get()
            send_to_server(data)

    # Start producer thread, then consume
    def run():
        try:
            self.running = True
            producer = Thread(target=self.produce)
            producer.start()
            self.consume()
        finally:
            self.running = False

如果将self.running设置为False,则上述代码的produce方法仍然会在read_from_kernel内阻塞,直到下一次返回,然后才会退出,但是Python几乎无法做到这一点。无论您使用何种系统调用都必须以某种方式支持:例如,如果它是实际的read,您的选项将包括:

  • 短暂超时,加上重试处理
  • 非阻止I / O(但在这种情况下,您可能需要调查基于此的框架,例如Twisted Python