如何在while循环中停止gevent微线程?

1 投票
3 回答
6169 浏览
提问于 2025-04-18 05:39

我遇到了一个问题,情况就像下面的代码一样。目标函数(wait)在一个循环里运行,或者花了很长时间,所以我想结束这个微线程。请问怎么才能结束线程1呢?

注意:用户的微线程可能是一个占用CPU的操作(比如一个没有任何输入输出操作的循环)。

import gevent
from gevent import Timeout

def wait():
    while(1):
        pass
    print 'end'


timer = Timeout(1).start()
thread1 = gevent.spawn(wait)

try:
    thread1.join(timeout=timer)
except Timeout:
    print('Thread 1 timed out')

无论是 gevent.killall() 还是 thread.kill() 都需要 线程调度器 来运行,它们并不会像我们预期的那样结束线程。

3 个回答

-2

我遇到了同样的问题,我需要终止一个用户编写代码的绿色线程(greenlet)。

如果是CPU密集型操作,Gevent的切换和超时功能将无法正常工作!

我们不能假设用户写的代码是正确的,里面没有死循环或者CPU密集型的操作。

最后,我使用了“信号”API来解决这个问题。

请参考 https://docs.python.org/2/library/signal.html

关于Gevent的切换和超时功能,可以查看这个链接: https://groups.google.com/forum/#!topic/gevent/eBj9YQYGBbc

下面的代码可能对你有帮助。

from gevent.monkey import patch_all;patch_all()
import gevent
import time


def fucking_loop():
    while True:
        pass


def fucking_loop_with_timeout():
    with gevent.Timeout(10):
        while True:
            pass


def fail_to_kill():
    deadline = time.time() + 10

    thread = gevent.spawn(fucking_loop)

    while deadline > time.time():
        # gevent.sleep will switch out of main thread
        gevent.sleep(1)

    # This line of code will never been executed
    # because gevent will not switch to main thread.
    thread.kill()


def fail_to_kill_2():

    thread = gevent.spawn(fucking_loop)

    # Will never timeout because of the same reason
    fucking_loop_with_timeout()

    # This line of code will never been executed
    # because gevent will not switch to main thread.
    thread.join(timeout=10)
    thread.kill()


def kill_fucking_loop_works():
    import signal
    timeout = 10

    thread = gevent.spawn(fucking_loop)

    def kill_thread(*args, **kwargs):
        if not thread.dead:
            thread.kill(timeout=1)

    signal.signal(signal.SIGALRM, kill_thread)
    signal.alarm(int(timeout))
0

你的代码只有在“等待”的微线程有机会切换时才能正常工作。如果你能控制你的线程代码,可以通过调用 gevent.sleep 来实现这个切换。否则,如果你的线程代码是用 Python 写的,并且使用了输入输出操作,你可以尝试通过以下方式来修改 Python 的函数:

from gevent.monkey import patch_all
patch_all()

这样可以让你的线程在遇到不同的输入输出操作时进行切换。

0

你在这里做了很多不必要的工作。其实你可以直接把秒数作为关键字参数传给 join(),像下面这样:

>>> import gevent
>>> def wait():
...    while True:
...        print("In loop")
...        gevent.sleep(1) # To not fill the screen..
...       
>>>
>>> g = gevent.spawn(wait)
>>> g.join(timeout=5) 

下面的代码会在5秒后终止正在运行 wait() 的绿色线程。你 无法 捕捉到 Timeout,因为 join() 会为你处理这个问题,并在超时的情况下默默地终止绿色线程。以下是来自 greenlet.pyjoin() 的一段代码:

try:
    t = Timeout.start_new(timeout)
    ... SNIP ...
except Timeout as ex:
    self.unlink(switch)
    if ex is not t:
        raise

你也可以使用 kill() 来终止一个绿色线程。我认为这种方法不太对,但我还是会给出例子。下面的代码会运行绿色线程5秒,然后将其终止:

>>> g = gevent.spawn(wait)
>>> with gevent.Timeout(5, False):
...     g.join()
... g.kill()
...

撰写回答