python高级编程(五)--多线程-协程
来源:互联网 发布:西门子tia博途软件 编辑:程序博客网 时间:2024/05/23 00:45
1. 简单协程
概念:协程,又称为微线程,纤程,英文名Coroutine。协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。
简单实现协程:
import timedef work1(): while True: print("------work1----") yield time.sleep(0.5)def work2(): while True: print("------work2-----") yield time.sleep(0.5)def main(): w1 = work1() w2 = work2() while True: next(w1) next(w2)if __name__ == "__main__": main()
运行结果:
—-work1—
—-work2—
—-work1—
—-work2—
—-work1—
—-work2—
…省略…
协程的好处:
- 无需线程上下文切换的开销
- 无需原子操作锁定及同步的开销
- 方便切换切换控制流,简化编程模型
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
- 无法利用核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上。当然我们日常所编写的绝大部分应用都没有这个必要,除非是CPU密集型应用。
- 进行阻塞(Blocking) 操作(如IO时)会阻塞掉整个程序。
2. greenlet
为了更好使用协程来完成多任务,python中greenle模块对其封装,从而使得切换任务变得更加简单。
- greenlet的使用:
# coding=utf-8from greenlet import greenletimport timedef test1(): whle True: print("---A----") gr2.switch() time.sleep(0.5)def test2(): while True: print("----B----") gr1.switch() time.sleep(0.5)gr1 = greenlet(test1)gr2 = greenlet(test2)# 切换到gr1中运行gr1.switch()
运行效果:
—A–
—B–
—A–
—B–
—A–
—B–
…省略…
3. gevent
gevent是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式greenlet,它是以c扩展模块形式介入python轻量级协程。greenlet全部运行在主程序操作系统进程的内部,但它们被协助式地调度。
- 能够自动切换任务的模块gevent
- 其原理是当一个greenlet遇到IO(指的是input output输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动化切换协程,就保证总有greenlet在运行,而不是等待IO
gevent使用
import geventdef f(n) for i in range(n): print(gevent.getcurrent(), i)g1 = gevent.spawn(f, 5)g2 = gevent.spawn(f, 5)g3 = gevent.spawn(f, 5)g1.join()g2.join()g3.join()
运行结果
<Greenlet at 0x10e49f550: f(5)> 0<Greenlet at 0x10e49f550: f(5)> 1<Greenlet at 0x10e49f550: f(5)> 2<Greenlet at 0x10e49f550: f(5)> 3<Greenlet at 0x10e49f550: f(5)> 4<Greenlet at 0x10e49f910: f(5)> 0<Greenlet at 0x10e49f910: f(5)> 1<Greenlet at 0x10e49f910: f(5)> 2<Greenlet at 0x10e49f910: f(5)> 3<Greenlet at 0x10e49f910: f(5)> 4<Greenlet at 0x10e49f4b0: f(5)> 0<Greenlet at 0x10e49f4b0: f(5)> 1<Greenlet at 0x10e49f4b0: f(5)> 2<Greenlet at 0x10e49f4b0: f(5)> 3<Greenlet at 0x10e49f4b0: f(5)> 4
- gevent自动切换任务
import geventdef f(n): for i in range(n): print(gevent.getcurrent(), i) # 用来模拟一个耗时操作,注意不是time模块中的sleep gevent.sleep(1)g1 = gevent.spawn(f, 5)g2 = gevent.spawn(f, 5)g3 = gevent.spawn(f, 5)g1.join()g2.join()g3.join()
运行结果:
<Greenlet at 0x7fa70ffa1c30: f(5)> 0<Greenlet at 0x7fa70ffa1870: f(5)> 0<Greenlet at 0x7fa70ffa1eb0: f(5)> 0<Greenlet at 0x7fa70ffa1c30: f(5)> 1<Greenlet at 0x7fa70ffa1870: f(5)> 1<Greenlet at 0x7fa70ffa1eb0: f(5)> 1<Greenlet at 0x7fa70ffa1c30: f(5)> 2<Greenlet at 0x7fa70ffa1870: f(5)> 2<Greenlet at 0x7fa70ffa1eb0: f(5)> 2<Greenlet at 0x7fa70ffa1c30: f(5)> 3<Greenlet at 0x7fa70ffa1870: f(5)> 3<Greenlet at 0x7fa70ffa1eb0: f(5)> 3<Greenlet at 0x7fa70ffa1c30: f(5)> 4<Greenlet at 0x7fa70ffa1870: f(5)> 4<Greenlet at 0x7fa70ffa1eb0: f(5)> 4
from gevent import monkeyimport geventimport randomimport time# 有耗时操作时需要monkey.patch_all() # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块def coroutine_work(coroutine_name): for i in range(10): print(coroutine_name, i) time.sleep(random.random())gevent.joinall([ gevent.spawn(coroutine_work, "work1"), gevent.spawn(coroutine_work, "work2")])
运行结果:
运行结果work1 0work2 0work1 1work1 2work1 3work2 1work1 4work2 2work1 5work2 3work1 6work1 7work1 8work2 4work2 5work1 9work2 6work2 7work2 8work2 9
- 通过gevent实现单线程下的多socket并发
server side
import sysimport socketimport timeimport geventfrom gevent import socket,monkeymonkey.patch_all()def server(port): s = socket.socket() s.bind(('0.0.0.0', port)) s.listen(500) while True: cli, addr = s.accept() gevent.spawn(handle_request, cli)def handle_request(conn): try: while True: data = conn.recv(1024) print("recv:", data) conn.send(data) if not data: conn.shutdown(socket.SHUT_WR) except Exception as ex: print(ex) finally: conn.close()if __name__ == '__main__': server(8001)
client side
import socketHOST = 'localhost' # The remote hostPORT = 8001 # The same port as used by the servers = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((HOST, PORT))while True: msg = bytes(input(">>:"),encoding="utf8") s.sendall(msg) data = s.recv(1024) #print(data) print('Received', repr(data))s.close()
阅读全文
1 0
- python高级编程(五)--多线程-协程
- python多线程编程(五)
- python高级编程(一)--多线程编程
- python高级编程--多线程
- Python高级编程(五)XML解析
- Python高级编程(四)多线程
- Python 高级编程--多线程编程(四)
- Python 多线程(高级教程)
- POSIX多线程程序设计学习篇之五(线程高级编程)
- Python高级编程——12.系统编程(多进程和多线程)总述
- Python自动化(五)多线程
- 多线程编程笔记(五)
- linux多线程编程(五)
- 多线程编程--多线程间通信(五)
- ASP 3.0高级编程(五)
- 《Unix环境高级编程》总结(五)
- (五)面向对象高级编程
- C++高级编程(五)模板
- 用C++和EasyX图形库编写一个简单的打砖块游戏(上)
- 分类学习
- 谈谈读书
- NOIP2016 Day2 T2 天天爱跑步(树上差分)
- UVA 201 Squares(暴力)
- python高级编程(五)--多线程-协程
- matplotlib画图1
- 最邻近规则分类(K-Nearest Neighbor)KNN算法应用
- java(146)IO流原理,分类,标准步骤
- Proxy for multiple cloud instances with multiple portals
- UnicodeEncodeError: 'ascii' code
- 问题org.springframework.orm.hibernate5.SessionHolder cannot be cast to org.springframework.orm.hiberna
- JVM内存设置详解
- 设置自启动脚本