龙卷风的IOLoop.call_later是令人费解的行为

问题描述:

从一个解释器会话:龙卷风的IOLoop.call_later是令人费解的行为

>>>io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, "google.com", lambda x: print('kek'))) 
<tornado.ioloop._Timeout object at 0x7fe9a2427b08> 
>>> io_loop.start() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 755, in start 
    raise RuntimeError("IOLoop is already running") 
RuntimeError: IOLoop is already running 
>>> io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, "google.com", lambda x: print('kek'))) 
<tornado.ioloop._Timeout object at 0x7fe9a0267808> 
>>> io_loop.stop() 
>>> io_loop.start() 
>>> io_loop.start() 
kek 
kek 
kek 

这里:

io_loop = tornado.ioloop.IOLoop.current() 
comments_page_delay = 0.1 

编辑:

AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") 
http_client = AsyncHTTPClient() 
http_client.max_clients = max_clients 
http_client.request_timeout = request_timeout 

问题:

的ioloop没有按”吨似乎运行与加ll_later。它正在运行,正如我在尝试执行io_loop.start()时显示的那样。为了让它真正执行,我必须先停止循环,然后启动它两次 ...非常混乱。

我承认自从使用Tornado以来已经有一段时间了,但这基本上与我所知道的其他代码相同。

编辑:代码在第一次运行时正常工作,但在连续运行时不能正常工作。在这一点上需要运行io_loop.start()两次返回

下面是完整的代码(略审查):

"""...docstring...""" 
import functools 

import tornado 
from tornado.httpclient import AsyncHTTPClient 
from lxml import html as lh 

from config import comments_page_delay, max_clients, request_timeout 
from debug import log 

AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") 
http_client = AsyncHTTPClient() 
http_client.max_clients = max_clients 
http_client.request_timeout = request_timeout 

io_loop = tornado.ioloop.IOLoop.current() 

def x(y, proxy): 
    """...docstring...""" 
    def handle_response(response): 
     if response.error: 
      log('...message...' 
       .format(uid, response.error), 1) 
     else: 
      print('Worked') 
      data = response.body 

    comments_url = "...valid url..." 

    io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, comments_url, handle_response, 
                   proxy_host=proxy['host'], proxy_port=proxy['port'])) 

x(1, {'host': None, 'port': None}) 

io_loop.start() 
print('ok') 
io_loop.stop() 

我跑在Emacs的Python解释器会话中的代码,如果是不知何故相关。我会在一秒钟内尝试正常的python会话。

EDIT2:这里的,如果我在一个正常的Python解释器运行该代码会发生什么:

$ python -i fetch.py 
Worked 


Traceback (most recent call last): 
    File "fetch.py", line 34, in <module> 
    io_loop.start() 
    File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 863, in start 
    event_pairs = self._impl.poll(poll_timeout) 
KeyboardInterrupt 
>>> >>> >>> get_user_profile_link(1, {'host': None, 'port': None}) 
>>> 
>>> 
>>> io_loop.start() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 755, in start 
    raise RuntimeError("IOLoop is already running") 
RuntimeError: IOLoop is already running 
>>> io_loop.stop() 
>>> io_loop.start() 
>>> io_loop.start() 
Worked 

看来,io_loop.start()阻止?我不认为这可能是问题所在。我现在必须重新考虑我整个程序的架构。

+0

你使用什么版本的python和龙卷风? ioloop是否在另一个线程中运行? call_later不是线程安全的。 – kwarunek

+0

Python 3.6,最新版本的Tornado。 这里不确定你对线程安全的意义。我没有对线程做任何特殊的处理。 –

+0

从Tornado的文档add_timeout(也适用于call_later) - http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_timeout:请注意,从其他线程调用add_timeout并不安全。相反,您必须使用add_callback将控制权转移给IOLoop的线程,然后从那里调用add_timeout。 – kwarunek

未捕获的异常(在本例中为KeyboardInterrupt)将使IOLoop处于未定义状态。发生这种异常后无法重新启动IOLoop(因为它可能在任何时候出现,并且使IOLoop的内部不一致)。一般而言,假定该过程将在KeyboardInterrupt之后简单地退出。可以创建一个新的IOLoop并重新开始,但是您必须小心重新创建所有依赖它的对象。

不幸的是,这意味着在交互式解释器中使用Tornado实际上很困难。我通常用于开发Tornado应用程序的工作流程是使用python -m tornado.autoreload运行脚本,并让脚本在我编辑它时从头开始重新运行。