gevent: AssertionError: Impossible to call blocking function in the event loop callback

今天在用爬虫时gevent报了AssertionError: Impossible to call blocking function in the event loop callback

异常,很奇怪,难道是patch_socket惹的货,因为之前没有使用patch_socket是正常的,代码简化如下

import urllib
import gevent
from gevent.monkey import patch_socket
from gevent.hub import  get_hub

def f():
    r = urllib.urlopen("http://www.baidu.com/").read()
    print r[:10]

def timer(after, repeat, f):
    t = get_hub().loop.timer(after, repeat)
    t.start(f)
    return t

def run():
    patch_socket()
    timer(1, 5, f)
    gevent.sleep(100)

run()

这段代码就是每5秒调用一次f,f也就是很简单的打压百度首页前10个字符,各位看官在揭开答案请先想想为什么为这样?

我把异常栈也贴在下面,有助有分析

  File "C:\Python27\lib\httplib.py", line 772, in connect
    self.timeout, self.source_address)
  File "C:\Python27\lib\site-packages\gevent\socket.py", line 570, in create_connection
    for res in getaddrinfo(host, port, 0 if has_ipv6 else AF_INET, SOCK_STREAM):
  File "C:\Python27\lib\site-packages\gevent\socket.py", line 621, in getaddrinfo
    return get_hub().resolver.getaddrinfo(host, port, family, socktype, proto, flags)
  File "C:\Python27\lib\site-packages\gevent\resolver_thread.py", line 34, in getaddrinfo
    return self.pool.apply_e(self.expected_errors, _socket.getaddrinfo, args, kwargs)
  File "C:\Python27\lib\site-packages\gevent\threadpool.py", line 222, in apply_e
    success, result = self.spawn(wrap_errors, expected_errors, function, args, kwargs).get()
  File "C:\Python27\lib\site-packages\gevent\event.py", line 226, in get
    result = self.hub.switch()
  File "C:\Python27\lib\site-packages\gevent\hub.py", line 330, in switch
    switch_out()
  File "C:\Python27\lib\site-packages\gevent\hub.py", line 334, in switch_out
    raise AssertionError(‘Impossible to call blocking function in the event loop callback‘)
AssertionError: Impossible to call blocking function in the event loop callback
<timer at 0x2652ed0 callback=<function f at 0x026B0070> args=()> failed with AssertionError

刚开始我百思不得其解,就这么简单为什么会有问题?

看异常栈是调用hub的switch_out出的问题,

    def switch(self):
        switch_out = getattr(getcurrent(), ‘switch_out‘, None)
        if switch_out is not None:
            switch_out()
        return greenlet.switch(self)

    def switch_out(self):
        raise AssertionError(‘Impossible to call blocking function in the event loop callback‘)

以前文章提过,gevent提供了switch_out方法用于当前greenlet换出时调用,咦,可为什么调用的hub的

switch_out?按理说应该调用其它greenlet的switch_out,怪不得有问题,hub都被换出了,谁去做调度呢?

问题就出在这里?你有没有发现,在上面的代码中只有hub,压根没有其它的greenlet。

我们走一遍代码逻辑,首先给系统注册一定时器f,当调用f时由于socket阻塞,所以会切换到hub,此时会调用之前greenlet的switch_out方法,可不幸的是之前的greenlet就是hub,所以出问题了。

知道了问题所在就好解决了,也就是用一个greenlet包装一下f,代码如下:

import urllib
import gevent
from gevent.monkey import patch_socket
from gevent.hub import  get_hub

def patch_greenlet(f):
    def inner(*args, **kwargs):
         return gevent.spawn(f, *args, **kwargs)
    return inner

@patch_greenlet
def f():
    r = urllib.urlopen("http://www.baidu.com/").read()
    print r[:10]

def timer(after, repeat, f):
    t = get_hub().loop.timer(after, repeat)
    t.start(f)
    return t

def run():
    patch_socket()
    timer(1, 0, f)
    gevent.sleep(100)

run()

不得不说使用gevent会碰到很多问题,这也许就是协成让人痴迷的一个原因吧,享受"找虐"的兴趣,越享受,越能驾驭它。

时间: 2024-08-09 15:35:05

gevent: AssertionError: Impossible to call blocking function in the event loop callback的相关文章

libev

Index NAME SYNOPSIS EXAMPLE PROGRAM ABOUT THIS DOCUMENT WHAT TO READ WHEN IN A HURRY ABOUT LIBEV FEATURES CONVENTIONS TIME REPRESENTATION ERROR HANDLING GLOBAL FUNCTIONS FUNCTIONS CONTROLLING EVENT LOOPS ANATOMY OF A WATCHER GENERIC WATCHER FUNCTIONS

《游戏脚本的设计与开发》-(RPG部分)3.8 通过脚本来自由控制游戏(一)

注意:本系列教程为长篇连载无底洞,半路杀进来的朋友,如果看不懂的话,请从第一章开始看起,文章目录请点击下面链接. http://blog.csdn.net/lufy_legend/article/details/8888787 一,内容预览 算起来,游戏脚本系列文章已经很久没更新了,虽然该系列文章更新缓慢,但是确实还是能够帮到一些朋友,前段时间,仅仅因为做毕业设计通过邮件联系我的就有4位学生.有鉴于此,我还是挤点儿时间来继续慢慢更新一下了.另外,我想再声明一下,目前该脚本引擎还处在移植开发阶段,

Live555 分析(三):客服端

live555的客服端流程:建立任务计划对象--建立环境对象--处理用户输入的参数(RTSP地址)--创建RTSPClient实例--发出DESCRIBE--发出SETUP--发出PLAY--进入Loop循环接收数据--发出TEARDOWN结束连接. 可以抽成3个函数接口:rtspOpen rtspRead rtspClose. 首先我们来分析rtspOpen的过程: int rtspOpen(rtsp_object_t *p_obj, int tcpConnect) { ... ... TRA

多媒体开发之---live555 分析客户端

live555的客服端流程:建立任务计划对象--建立环境对象--处理用户输入的参数(RTSP地址)--创建RTSPClient实例--发出DESCRIBE--发出SETUP--发出PLAY--进入Loop循环接收数据--发出TEARDOWN结束连接. 可以抽成3个函数接口:rtspOpen rtspRead rtspClose. 首先我们来分析rtspOpen的过程: int rtspOpen(rtsp_object_t *p_obj, int tcpConnect) { ... ... TRA

redis学习笔记——Redis过期键的删除策略

Redis过期键的删除策略 对于过期键一般有三种删除策略 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作: 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键:如果没有过期,那就返回该键: 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键.至于删除多少过期键,以及要检查多少个数据库,则由算法决定. 下面我们来看看三种策略的优缺比较: 定时删除策略对内存是

《Redis设计与实现》[第二部分]单机数据库的实现-C源码阅读(一)

1.数据库 关键字:键空间,过期,删除策略 数据结构源码 //redisServer中属性太多,篇幅限制,故只列本章描述相关的属性 struct redisServer { //... // 数据库 //一个数组,保存着服务器中的所有数据库 redisDb *db; // 服务器的数据库数量 int dbnum; //.. } ; Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDb结构,每个

[gevent源码分析] libev cython绑定core.pyx

gevent core就是封装了libev,使用了cython的语法,感兴趣童鞋可以好好研究研究.其实libev是有python的封装 pyev(https://pythonhosted.org/pyev/),不过pyev是使用C来写扩展的,代码巨复杂.在看core.pyx代码之前先学习一下 core.pyx用到的cython知识. 一: cython基础知识 1.cdef, def, cpdef的区别 cdef用于定义C中的函数,变量,如cdef int i;而def知识python中的函数定

Asynchronous function in a while-loop

I have a question about how to perform an asynchronous task in a while-loop until some condition is met. This is more of a theoretical question but I can see how this could be an issue in some scenarios. I'll try demonstrate the problem at an example

The App Life Cycle &amp; The Main Function

The App Life Cycle Apps are a sophisticated interplay between your custom code and the system frameworks. The system frameworks provide the basic infrastructure that all apps need to run, and you provide the code required to customize that infrastruc