【问题标题】:Why is a TimeoutError not being raised in concurrent.futures.Future instances为什么没有在 concurrent.futures.Future 实例中引发 TimeoutError
【发布时间】:2015-01-23 01:13:01
【问题描述】:

我基于来自https://docs.python.org/3/library/concurrent.futures.html#id1 的样本。

我已更新以下内容:
data = future.result()
对此:
data = future.result(timeout=0.1)

concurrent.futures.Future.result 的文档指出:

如果调用没有在 timeout 秒内完成,则会引发 TimeoutError。 timeout 可以是 int 或 float

(我知道请求超时,为 60,但在我的真实代码中,我正在执行不使用 urllib 请求的不同操作)

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    conn = urllib.request.urlopen(url, timeout=timeout)
    return conn.readall()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            # The below timeout isn't raising the TimeoutError.
            data = future.result(timeout=0.01)
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

如果我在对as_completed 的调用中设置它,则会引发TimeoutError,但我需要在每个 Future 的基础上设置超时,而不是全部设置。


更新

感谢@jme,它适用于单个 Future,但不适用于使用下面的多个 Future。我是否需要在函数开头使用yield 以允许构建futures 字典?从文档看来,对submit 的调用不应被阻止。

import concurrent.futures
import time
import sys

def wait():
    time.sleep(5)
    return 42

with concurrent.futures.ThreadPoolExecutor(4) as executor:
    waits = [wait, wait]
    futures = {executor.submit(w): w for w in waits}
    for future in concurrent.futures.as_completed(futures):
        try:
            future.result(timeout=1)
        except concurrent.futures.TimeoutError:
            print("Too long!")
            sys.stdout.flush()

print(future.result())

【问题讨论】:

    标签: python python-3.x concurrency


    【解决方案1】:

    问题似乎与对concurrent.futures.as_completed() 的调用有关。

    如果我只用 for 循环替换它,一切似乎都正常:

    for wait, future in [(w, executor.submit(w)) for w in waits]:
        ...
    

    我误解了as_completed 的文档,其中指出:

    ...在期货完成(完成或被取消)时产生期货...

    as_completed 将处理超时,但作为一个整体,而不是在每个未来的基础上。

    【讨论】:

    • 发布完整的解决方案会很有帮助
    【解决方案2】:

    异常在主线程中引发,你只是没有看到它,因为stdout 还没有被刷新。举个例子:

    import concurrent.futures
    import time
    import sys
    
    def wait():
        time.sleep(5)
        return 42
    
    with concurrent.futures.ThreadPoolExecutor(4) as executor:
        future = executor.submit(wait)
        try:
            future.result(timeout=1)
        except concurrent.futures.TimeoutError:
            print("Too long!")
            sys.stdout.flush()
    
    print(future.result())
    

    运行这个,你会看到“太长了!”一秒钟后出现,但解释器将再等待四秒钟以使线程完成执行。然后你会看到42——wait()的结果——出现。

    这是什么意思?设置超时不会杀死线程,这实际上是一件好事。如果线程持有锁怎么办?如果我们突然杀死它,那锁永远不会被释放。不,最好让线程处理自己的死亡。同样,future.cancel 的目的是防止线程启动,而不是杀死它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多