Python gevent协程切换实现方法

  • 时间:
  • 7679人关注

这篇文章主要介绍了Python gevent协程切换实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下,另外这篇文章主要知识点是关于Python、gevent、协程切换、Python的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子资料:

一、背景

大家都知道gevent的机制是单线程+协程机制,当遇到可能会阻塞的操作时,就切换到可运行的协程中继续运行,以此来实现提交系统运行效率的目标,但是具体是怎么实现的呢?让我们直接从代码中看一下吧。

二、切换机制

让我们从socket的send、recv方法入手:

def recv(self, *args):
  while 1:
    try:
      return self._sock.recv(*args)
    except error as ex:
      if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
        raise
      # QQQ without clearing exc_info test__refcount.test_clean_exit fails
      sys.exc_clear()
    self._wait(self._read_event)

这里会开启一个死循环,在循环中调用self._sock.recv()方法,并捕获异常,当错误是EWOULDBLOCK时,则调用self._wait(self._read_event)方法,该方法其实是:_wait = _wait_on_socket,_wait_on_socket方法的定义在文件:_hub_primitives.py中,如下:

# Suitable to be bound as an instance method
def wait_on_socket(socket, watcher, timeout_exc=None):
  if socket is None or watcher is None:
    # test__hub TestCloseSocketWhilePolling, on Python 2; Python 3
    # catches the EBADF differently.
    raise ConcurrentObjectUseError("The socket has already been closed by another greenlet")
  _primitive_wait(watcher, socket.timeout,
          timeout_exc if timeout_exc is not None else _NONE,
          socket.hub)

该方法其实是调用了函数:_primitive_wait(),其仍然在文件:_hub_primitives.py中定义,如下:

def _primitive_wait(watcher, timeout, timeout_exc, hub):
  if watcher.callback is not None:
    raise ConcurrentObjectUseError('This socket is already used by another greenlet: %r'
                    % (watcher.callback, ))

  if hub is None:
    hub = get_hub()

  if timeout is None:
    hub.wait(watcher)
    return

  timeout = Timeout._start_new_or_dummy(
    timeout,
    (timeout_exc
     if timeout_exc is not _NONE or timeout is None
     else _timeout_error('timed out')))

  with timeout:
    hub.wait(watcher)

这里其实是调用了hub.wait()函数,该函数的定义在文件_hub.py中,如下:

class WaitOperationsGreenlet(SwitchOutGreenletWithLoop): # pylint:disable=undefined-variable

  def wait(self, watcher):
    """
    Wait until the *watcher* (which must not be started) is ready.

    The current greenlet will be unscheduled during this time.
    """
    waiter = Waiter(self) # pylint:disable=undefined-variable
    watcher.start(waiter.switch, waiter)
    try:
      result = waiter.get()
      if result is not waiter:
        raise InvalidSwitchError(
          'Invalid switch into %s: got %r (expected %r; waiting on %r with %r)' % (
            getcurrent(), # pylint:disable=undefined-variable
            result,
            waiter,
            self,
            watcher
          )
        )
    finally:
      watcher.stop()

watcher.stop()

该类WaitOperationsGreenlet是Hub的基类,其方法wait中的逻辑是:生成一个Waiter对象,并调用watcher.start(waiter.switch, waiter)方法,watcher是最开始recv方法中使用的self._read_event,watcher是gevent的底层事件框架libev中的概念;同时还有一个waiter对象,它类似与python中的future概念,该对象有一个switch()方法以及get()方法,当没有得到结果没有准备好时,调用waiter.get()方法回导致协程被挂起;get()函数的定义如下:

def get(self):
  """If a value/an exception is stored, return/raise it. Otherwise until switch() or throw() is called."""
  if self._exception is not _NONE:
    if self._exception is None:
      return self.value
    getcurrent().throw(*self._exception) # pylint:disable=undefined-variable
  else:
    if self.greenlet is not None:
      raise ConcurrentObjectUseError('This Waiter is already used by %r' % (self.greenlet, ))
    self.greenlet = getcurrent() # pylint:disable=undefined-variable
    try:
      return self.hub.switch()
    finally:
      self.greenlet = None

在get()中最关键的是self.hub.switch()函数,该函数将执行权转移到hub,并继续运行,至此已经分析完了当在worker协程中从网络获取数据遇到阻塞时,如何避免阻塞并切换到hub中的实现,至于何时再切换会worker协程,我们后续再继续分析。

总结

要记得gevent中一个重要的概念,协程切换不是调用而是执行权的转移,从可能会阻塞的协程切换到hub,并由hub在合适的时机切换到另一个可以继续运行的协程继续执行;gevent通过这种形式实现了提高io密集型应用吞吐率的目标。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。


上一篇:python/golang实现循环链表的代码

下一篇:python实现可视化的MD5、sha256哈希加密小工具

相关内容

  • Python如何限制输出日志的大小

    这篇文章主要介绍了Python 限制输出日志的大小方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    05-06Python限制日志大小

    阅读更多
  • python实现可视化的MD5、sha256哈希加密小工具

    这篇文章主要介绍了详解python实现可视化的MD5、sha256哈希加密小工具,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    04-07python MD5、sha256哈希加密

    阅读更多
  • python/golang实现循环链表的代码

    这篇文章主要介绍了python/golang如何实现循环链表,帮助大家更好的理解和学习循环链表的实现方法,感兴趣的朋友可以了解下

    04-07python/golang 循环链表

    阅读更多
  • 2020版Python学习路线图(附学习资料)

    这篇文章主要介绍了Python学习路线图(2020最新版),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    04-07Python学习路线图

    阅读更多
  • python实现复制文件并重命名

    这篇文章主要介绍了基于python实现复制文件并重命名,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    04-08python复制重命名

    阅读更多
  • 数据科学导论:Python语言实现

    数据科学导论:Python语言实现

    《数据科学导论:Python语言实现》 由两位资深数据科学家撰写,融合其多年从事数据科学相关的教学和科研工作经验,借助现有的Python语法和结构知识,全面而系统地讲解进行数据科学分析和开

    大小:30.2 MBPython

    点击下载
  • Python王者归来

    Python王者归来

    Python 的丰富模块(module)以及广泛的应用范围,使 Python 成为当下最重要的计算机语言之一, 本书尝试将所有常用模块与应用分门别类组织起来,相信只要读者遵循本书实例,一定可以轻松学会 Python 语法与应用,逐步向 Python 高手之路迈进,这也是撰写本书的目的。

    大小:270735 MB Python

    点击下载
  • Python机器学习实践指南

    Python机器学习实践指南

    机器学习是近年来渐趋热门的一个领域,同时Python 语言经过一段时间的发展也已逐渐成为主流的编程语言之一。 Python机器学习实践指南 结合了机器学习和Python 语言两个热门的领域,通过利用

    大小:4.5 MBPython

    点击下载
  • Python游戏编程快速上手(第4版)

    Python游戏编程快速上手(第4版)

    本书通过小巧有趣的游戏来教授Python编程,采用直接展示游戏的源代码并通过实例来解释编程的原理的方式,帮助读者掌握Python游戏编程的基本技能,适合不同年龄和层次的Python编程初学者阅读

    大小:41.2 MBPython

    点击下载
  • Python程序设计入门到实战

    Python程序设计入门到实战

    Python言语是现在市面上*火爆的程序设计语言之一,除开功能齐全以外,也有迅速入门、随时随地可扩大、社群营销兼容等特点。这书以16章的篇数迅速详细介绍Python言语的精要,包含编程设计

    大小:423.1 MBPython

    点击下载
  • Python程序设计与算法基础教程

    Python程序设计与算法基础教程

    大小:107.6 MBPython编程

    点击下载

学习笔记

27小时37分钟前回答

简单了解python gevent 协程使用及作用

简介 没有切换开销。因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高, 不需要锁机制。因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多 Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。 yield 传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。 如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高 代码……

30小时54分钟前回答

python协程gevent案例 爬取斗鱼图片过程解析

分析 分析网站寻找需要的网址 用谷歌浏览器摁F12打开开发者工具,然后打开斗鱼颜值分类的页面,如图: 在里面的请求中,最后发现它是以ajax加载的数据,数据格式为json,如图: 圈住的部分是我们需要的数据,然后复制它的网址为https://www.douyu.com/gapi/rknc/directory/yzRec/1,出于学习目的只爬取第一页(减少服务器压力)。然后把网址放到浏览器中测试是否可以访问。如图: 结果正常。 分析json数据,提取图片链接 最后分析发现json中的data里面的rl是每个房间的信息,大概有200条左右,拿出其中的一条查询里面的图片链接。 { "rid": 1282190, "rn": "大家要开心啊~", "uid": 77538371, "nn": "鲸鱼欧尼", "cid1": 8, "cid2"……