首页 > 编程语言 >Python并发编程的协程

Python并发编程的协程

时间:2024-02-27 17:25:11浏览次数:26  
标签:协程 socket Python 编程 阻塞 gevent 线程 asyncio

一、协程的概念

1、协程是单线程下的并发,它是程序员级别的,我们来控制如何切换。

2、进程的开销 >>>>>> 线程的开销 >>>>>> 协程的开销

3、协程的使用需要借助于第三方模块 gevent 模块或者 asyncio 模块

4、gevent 和 asyncio 是两个在 Python 中常用的协程框架,它们都提供了在异步编程中使用协程的能力。

  gevent 是一个基于协程的并发库,它通过 monkey-patching 的方式将标准库中的阻塞式 I/O 操作变为非阻塞的,从而实现协程的调度和并发处理。gevent 提供了自己的协程对象 Greenlet,使用 gevent.spawn() 来创建和管理协程。gevent 还提供了事件循环机制,允许在协程之间进行切换和调度。它适用于需要高性能网络编程的场景,如服务器开发。

  asyncio 是 Python 官方引入的异步编程框架,从 Python 3.4 版本开始引入,并在 Python 3.5 版本引入了 async 和 await 关键字。asyncio 基于事件循环(event loop)的概念,使用协程来实现异步编程。

它提供了异步 I/O 操作、定时器、任务调度等功能,并且有丰富的标准库和第三方库支持。asyncio 适用于编写高效的异步代码,特别是在 Web 开发、网络爬虫等场景下。

下面是一些比较 gevent 和 asyncio 的特点:

  • 生态系统和库支持: asyncio 有着更广泛的生态系统和库支持,包括官方标准库和第三方库,可以方便地与其他异步框架集成。gevent 虽然也有一些扩展库,但相对较少。

  • 编程模型和语法: gevent 使用了更传统的基于线程和阻塞 I/O 的编程模型,通过 monkey-patching 来实现协程。而 asyncio 使用了更现代的基于事件循环和异步 I/O 的编程模型,通过 async 和 await 关键字来定义协程。

  • 性能和吞吐量: 由于 gevent 使用了更轻量级的协程对象和调度机制,并且对阻塞 I/O 进行了优化,因此在某些特定的场景下,它可能会比 asyncio 更高效和具有更高的吞吐量。

综上所述,选择使用 gevent 还是 asyncio 取决于具体的需求和场景。如果你需要高性能的网络编程和并发处理,可以选择使用 gevent。而如果你需要一个官方支持、成熟且广泛应用的异步编程框架,并且需要与其他库和框架无缝集成,那么选择 asyncio 是一个不错的选择。

二、gevent的使用

1、服务端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from gevent import monkey monkey.patch_all() import gevent import socket   def talk(conn):     while True:         try:             data = conn.recv(1024)             if len(data) == 0:                 break             print(data)             conn.send(data.upper())         except Exception as e:             print(e)     conn.close()     def server(ip, port):     server = socket.socket()  # 实例化一个服务端     server.bind((ip, port))     server.listen(5)     while True:         conn, addr = server.accept()         gevent.spawn(talk, conn)  # gevent.spawn() 函数用于创建并启动一个协程,并返回一个 Greenlet 对象         # t=Process(target=talk,args=(conn,))         # t=Thread(target=talk,args=(conn,))         # t.start()     if __name__ == '__main__':     g1 = gevent.spawn(server, '127.0.0.1'8080)  # gevent.spawn() 函数用于创建并启动一个协程,并返回一个 Greenlet 对象     g1.join()   ''' b'Thread-432 say hello' b'Thread-431 say hello' b'Thread-427 say hello' b'Thread-489 say hello' '''

猴子补丁的补充:

monkey.patch_all() 是 gevent 库中的一个函数,它的作用是将标准库中的阻塞式 I/O 操作替换为非阻塞的版本,从而实现协程的调度和并发处理。

具体解释如下:

  1. gevent 使用协程来实现并发处理,而协程的调度是由程序自身控制的。然而,标准库中的某些 I/O 操作(如 socket、select 等)是阻塞式的,它们会阻塞当前线程的执行,导致协程无法进行切换和调度。

  2. 为了解决这个问题,gevent 提供了 monkey.patch_all() 函数。调用 patch_all() 函数后,它会动态地修改标准库中的相关模块,将阻塞式的 I/O 操作替换为非阻塞的版本,从而实现协程的调度和并发处理。

  3. monkey.patch_all() 函数会自动替换标准库中的一些模块,包括 socket、select、threading 等。它会将这些模块的阻塞式 I/O 操作替换为非阻塞的版本,使得协程可以在 I/O 操作阻塞时切换到其他协程执行,从而提高并发性能。

需要注意以下几点:

  • monkey.patch_all() 函数应该在程序的入口处调用,通常是在导入其他模块之前。这样可以确保所有的阻塞式 I/O 操作都被正确地替换。

  • monkey.patch_all() 函数会修改全局的 Python 环境,因此需要谨慎使用,特别是在与其他库和框架的集成中。如果某个库依赖于标准库的阻塞式 I/O 操作,可能会受到 patch_all() 的影响。

总结起来,monkey.patch_all() 函数是 gevent 库中的一个工具函数,用于将标准库中的阻塞式 I/O 操作替换为非阻塞的版本,从而实现协程的调度和并发处理。它是使用 gevent 进行协程编程的常用方法之一,能够提高并发性能和效率。

2、客户端

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import socket from threading  import current_thread, Thread   def socket_client():     client = socket.socket()     client.connect(('127.0.0.1'8080))     while True:         ss = '%s say hello' % current_thread().getName()         client.send(ss.encode('utf-8'))         data = client.recv(1024)         print(data)   # 创建500个线程 for i  in range(500):     = Thread(target=socket_client)  # 实例化一个线程客户端     t.start()   ''' b'THREAD-171 SAY HELLO' b'THREAD-416 SAY HELLO' b'THREAD-384 SAY HELLO' b'THREAD-37 SAY HELLO' ''' 

current_thread 是 threading 模块中的一个函数,用于获取当前正在执行的线程对象。

具体含义如下:

  • 当你调用 current_thread() 函数时,它会返回当前线程的 Thread 对象。这个对象代表了当前执行的线程,可以通过它来获取线程的一些属性和调用线程的方法。

  • Thread 对象具有各种属性和方法,例如 name(线程的名称)、ident(线程的标识符)、is_alive()(判断线程是否存活)等。通过 current_thread() 返回的对象,你可以使用这些属性和方法来了解和操作当前线程的状态和行为。

  • current_thread() 函数可以在任何地方调用,包括主线程、子线程或其他线程的上下文中。它总是返回调用时的当前线程对象。

标签:协程,socket,Python,编程,阻塞,gevent,线程,asyncio
From: https://www.cnblogs.com/Jessica-Jmm/p/18037316

相关文章

  • Python脚本给数据库插入数据报错: raise errorclass(errno, errval) pymysql.err.Integ
    一、问题描述在SQL客户端插入数据时执行SQL语句INSERTINTOuser_xxx(id,uid,total,balance,wallet_type,ctime,utime)VALUES(0,'336448391',271,89000098,'coin',1708588247,1709007766);可以成功执行没报错;使用python脚本执行SQL语句INSERTINTOuser_xxx......
  • python-dotenv模块的使用
    安装模块pip3installpython-dotenv使用方式需要在根路径下新建.env文件,并写入配置importosfromdotenvimportload_dotenvfromdotenvimportdotenv_values###方法一res=load_dotenv()#takeenvironmentvariablesfrom.env#返回True或Falseprint(res......
  • python 的深浅拷贝
    python的深浅拷贝在Python中,深拷贝(deepcopy)和浅拷贝(shallowcopy)是用于复制数据结构(如列表或字典)的两种不同方式,它们有以下区别:浅拷贝(ShallowCopy):浅拷贝创建一个新的对象,然后将原始对象中的元素(如果是可变对象)复制到新对象中。但是,如果元素本身也是一个可变对象,则新对象中的......
  • python中的列表和元组有什么区别
    python中的列表和元组有什么区别在Python中,列表(List)和元组(Tuple)都是用来存储一组有序元素的数据结构,它们之间有几个重要的区别:可变性:列表是可变的(Mutable),意味着你可以改变列表中的元素,包括添加、删除、修改元素。元组是不可变的(Immutable),一旦创建后就无法修改。你不能在元组中......
  • 2024-02-27-物联网系统编程(7- 共享内存)
    7.共享内存7.1共享内存概述​共享内存允许两个或者多个进程共享给定的区域共享内存的特点共享内存是进程间共享数据的一种最快的方法;一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。使用共享内存要注意的是多个进程之......
  • 《安富莱嵌入式周报》第333期:F35战斗机软件使用编程语言占比,开源10V基准电源,不断电运
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版:https://www.bilibili.com/video/BV1y1421f7ip目录:1、F35战斗机软件使用编程语言占比2、开源10V基准电源,不断电运行一年,误差小于1uV3、资讯(1)苹果开源配置语言Pkl......
  • 爬取网页曲线图的后台数据——Python实现
    爬取网页曲线图数据的应用十分广泛。在市场分析领域,投资者可以通过分析金融网站上的股票曲线图数据来了解股市趋势,从而做出更明智的投资决策。在科学研究中,研究人员可以通过爬取科学期刊网站上的曲线图数据来分析实验结果,推动科学进步。在气象领域,气象学家可以通过爬取气象网站上......
  • python基础知识
    《跟着孙兴华学Python基础》一、常见数据类型字符串:不能修改的字符序列。除了不能修改,可把字符串当成列表一样处理。列表:我觉得列表就是我们日常生活中经常见到的清单。比如,统计过去一周我们买过的东西,把这些东西列出来,就是清单。由于我们买一种东西可能不止一次,所以清单中是允......
  • 使用python批量删除redis key
     比如我的业务。刚上线默认为超级管理员新增权限--请导出id用于清缓存svc格式请注意分页需要导出全部selectCONCAT('@rbac/ent/aclgr/',e.id)as需要清理缓存的rediskeyfroment_rbac_groupewherenotexists(selectp.`groupid`froment_rbac_group_permissionp......
  • Python 中动态调用函数或类的方法
    使用importlib#module.pyclassA:deffoo(self):print('thisisfoo.')@staticmethoddefstatic_method():print('thisisstatic.')defbar():print('bar……')defbaz():print('==......