Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4586924
  • 博文数量: 1214
  • 博客积分: 13195
  • 博客等级: 上将
  • 技术积分: 9105
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-19 14:41
个人简介

C++,python,热爱算法和机器学习

文章分类

全部博文(1214)

文章存档

2021年(13)

2020年(49)

2019年(14)

2018年(27)

2017年(69)

2016年(100)

2015年(106)

2014年(240)

2013年(5)

2012年(193)

2011年(155)

2010年(93)

2009年(62)

2008年(51)

2007年(37)

分类: Python/Ruby

2016-06-12 22:40:15

原文地址:http://blog.csdn.net/yueguanghaidao/article/details/41043375

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

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

[python] view plain copy
  1. import urllib  
  2. import gevent  
  3. from gevent.monkey import patch_socket  
  4. from gevent.hub import  get_hub  
  5.   
  6. def f():  
  7.     r = urllib.urlopen("").read()  
  8.     print r[:10]  
  9.   
  10. def timer(after, repeat, f):  
  11.     t = get_hub().loop.timer(after, repeat)  
  12.     t.start(f)  
  13.     return t  
  14.   
  15. def run():  
  16.     patch_socket()  
  17.     timer(15, f)  
  18.     gevent.sleep(100)  
  19.   
  20. run()  


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

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


[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.   File "C:\Python27\lib\httplib.py", line 772in connect  
  2.     self.timeout, self.source_address)  
  3.   File "C:\Python27\lib\site-packages\gevent\socket.py", line 570in create_connection  
  4.     for res in getaddrinfo(host, port, 0 if has_ipv6 else AF_INET, SOCK_STREAM):  
  5.   File "C:\Python27\lib\site-packages\gevent\socket.py", line 621in getaddrinfo  
  6.     return get_hub().resolver.getaddrinfo(host, port, family, socktype, proto, flags)  
  7.   File "C:\Python27\lib\site-packages\gevent\resolver_thread.py", line 34in getaddrinfo  
  8.     return self.pool.apply_e(self.expected_errors, _socket.getaddrinfo, args, kwargs)  
  9.   File "C:\Python27\lib\site-packages\gevent\threadpool.py", line 222in apply_e  
  10.     success, result = self.spawn(wrap_errors, expected_errors, function, args, kwargs).get()  
  11.   File "C:\Python27\lib\site-packages\gevent\event.py", line 226in get  
  12.     result = self.hub.switch()  
  13.   File "C:\Python27\lib\site-packages\gevent\hub.py", line 330in switch  
  14.     switch_out()  
  15.   File "C:\Python27\lib\site-packages\gevent\hub.py", line 334in switch_out  
  16.     raise AssertionError('Impossible to call blocking function in the event loop callback')  
  17. AssertionError: Impossible to call blocking function in the event loop callback  
  18. 0x2652ed0 callback=0x026B0070> args=()> failed with AssertionError  



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

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


[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. def switch(self):  
  2.     switch_out = getattr(getcurrent(), 'switch_out'None)  
  3.     if switch_out is not None:  
  4.         switch_out()  
  5.     return greenlet.switch(self)  
  6.   
  7. def switch_out(self):  
  8.     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,代码如下:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. import urllib  
  2. import gevent  
  3. from gevent.monkey import patch_socket  
  4. from gevent.hub import  get_hub  
  5.   
  6. def patch_greenlet(f):  
  7.     def inner(*args, **kwargs):  
  8.          return gevent.spawn(f, *args, **kwargs)  
  9.     return inner  
  10.  
  11. @patch_greenlet  
  12. def f():  
  13.     r = urllib.urlopen("").read()  
  14.     print r[:10]  
  15.   
  16. def timer(after, repeat, f):  
  17.     t = get_hub().loop.timer(after, repeat)  
  18.     t.start(f)  
  19.     return t  
  20.   
  21. def run():  
  22.     patch_socket()  
  23.     timer(10, f)  
  24.     gevent.sleep(100)  
  25.   
  26. run()  


不得不说使用gevent会碰到很多问题,这也许就是协成让人痴迷的一个原因吧,享受"找虐"的兴趣,越享受,越能驾驭它。
阅读(2368) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~