问题在于:我有一个线程,它从发生器运行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 ,这是不可能的,因为没有办法干净地暂停当前正在执行的生成器。
很抱歉这个混乱。
答案 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
,您的选项将包括: