requests.get 挂起的多处理

Multiprocessing hanging with requests.get

我一直在使用一段非常简单的代码,但行为却很奇怪。我正在尝试使用 requests.get 向网页发送请求,但如果请求花费的时间超过几秒钟,我想终止该进程。我正在关注接受的答案 here 的响应,但更改函数体以包含 request。我的代码如下:

import multiprocessing as mp, requests
def get_page(_r):                   
  _rs = requests.get('https://www.woolworths.com.au/shop/browse/drinks/cordials-juices-iced-teas/iced-teas').text
  _r.put(_rs)

q = mp.Queue() 
p = mp.Process(target=get_page, args=(q,))
p.start()
time.sleep(3)
p.terminate()
p.join()
try:
   result = q.get(False)
   print(result)
except:
   print('failed')

上面的代码在 运行ning 时简单地挂起。然而,当我 运行

requests.get('https://www.woolworths.com.au/shop/browse/drinks/cordials-juices-iced-teas/iced-teas').text

独立地,响应在两秒内返回。因此,主代码应该打印页面的 HTML,但是,它只是停止了。奇怪的是,当我在 get_page:

中放置一个无限循环时
def get_page(_r): 
  while True:
     pass
  _r.put('You will not see this')

进程确实在三秒后终止。因此,我确定该行为与 requests 有关。这怎么可能?我发现了一个类似的问题 here,但我没有使用 async。问题是否与猴子补丁有关,因为我正在使用 requests 以及 timemultiprocessing?任何建议或意见将不胜感激。谢谢!

我正在使用:

编辑:@Hitobat 指出参数 timeout 可以代替 requests 使用。这确实有效,但是,对于 requests 为什么 multiprocessing.

失败的任何其他想法,我将不胜感激

我已经重现了你的场景,我不得不反驳上述假设"I am certain the behavior has to do with requests"
requests.get(...) returns 响应符合预期。

让我们看看这个过程如何进行一些调试:

import multiprocessing as mp, requests
import time


def get_page(_r):
    _rs = requests.get('https://www.woolworths.com.au/shop/browse/drinks/cordials-juices-iced-teas/iced-teas').text
    print('--- response header', _rs[:17])
    _r.put(_rs)


q = mp.Queue()
p = mp.Process(target=get_page, args=(q,))
p.start()
time.sleep(3)
p.terminate()
p.join()

try:
    print('--- get data from queue of size', q.qsize())
    result = q.get(False)
    print(result)
except Exception as ex:
    print('failed', str(ex))

输出:

--- response header 
<!DOCTYPE html>
--- get data from queue of size 1

正如我们看到的那样,响应已经存在,进程甚至前进到 try 块语句,但它 hangs/stops 在语句 q.get() 试图从队列中提取数据时。 因此我们可以得出结论,队列很可能已损坏。 我们在 multiprocessing 库文档(Pipes and Queues 部分)中有相应的警告:

Warning

If a process is killed using Process.terminate() or os.kill() while it is trying to use a Queue, then the data in the queue is likely to become corrupted. This may cause any other process to get an exception when it tries to use the queue later on.

看来是这种情况。


我们如何处理这个问题?

已知的解决方法是使用 mp.Manager().Queue()(具有中间 代理 级别)而不是 mp.Queue:

...
q = mp.Manager().Queue()
p = mp.Process(target=get_page, args=(q,))