12.python进程\协程\异步IO
进程
Python中的多线程无法利用多核优势 , 所以如果我们想要充分地使用多核CPU的资源 , 那么就只能靠多进程了
multiprocessing模块中提供了Process , Queue , Pipe , Lock , RLock , Event , Condition等组件 , 与threading模块有很多相似之处
1.创建进程
from multiprocessing import Process
import time def func(name):
time.sleep(2)
print('hello',name) if __name__ == '__main__':
p= Process(target=func,args=('derek',))
p.start()
# p.join()
print('end...')
2.进程间通讯
(1)Queue
不同进程间内存是不共享的,要想实现两个进程间的数据交换。进程间通信有两种主要形式 , 队列和管道
from multiprocessing import Process, Queue #Queue是进程排列 def f(test):
test.put('') #通过创建的子进程往队列添加数据,实线父子进程交互 if __name__ == '__main__':
q = Queue() #父进程
q.put("") p = Process(target=f, args=(q,)) #子进程
p.start()
p.join() print("取到:",q.get_nowait())
print("取到:",q.get_nowait()) #父进程在创建子进程的时候就把q克隆一份给子进程
#通过pickle序列化、反序列化,来达到两个进程之间的交互 结果:
取到: 11
取到: 22
Queue
(2)Pipe(管道)
The Pipe()
function returns a pair of connection objects connected by a pipe which by default is duplex (two-way).
from multiprocessing import Process, Pipe def f(conn):
conn.send('')
conn.send('')
print("from parent:",conn.recv())
print("from parent:", conn.recv())
conn.close() if __name__ == '__main__':
parent_conn, child_conn = Pipe() #生成管道实例,可以互相send()和recv() p = Process(target=f, args=(child_conn,))
p.start() print(parent_conn.recv()) # prints "11"
print(parent_conn.recv()) # prints "22"
parent_conn.send("") # parent 发消息给 child
parent_conn.send("")
p.join()
Pipe
3.Manager
进程之间是相互独立的 ,Queue和pipe只是实现了数据交互,并没实现数据共享,Manager可以实现进程间数据共享 。
Manager还支持进程中的很多操作 , 比如Condition , Lock , Namespace , Queue , RLock , Semaphore等
from multiprocessing import Process, Manager
import os def f(d, l):
d[os.getpid()] =os.getpid()
l.append(os.getpid())
print(l) if __name__ == '__main__':
with Manager() as manager:
d = manager.dict() #{} #生成一个字典,可在多个进程间共享和传递 l = manager.list(range(5)) #生成一个列表,可在多个进程间共享和传递
p_list = []
for i in range(2):
p = Process(target=f, args=(d, l))
p.start()
p_list.append(p)
for res in p_list: #等待结果
res.join()
print(d)
print(l)
4.lock
from multiprocessing import Process, Lock def f(l, i):
#l.acquire()
print('hello world', i)
#l.release() if __name__ == '__main__':
lock = Lock() for num in range(100):
Process(target=f, args=(lock, num)).start() #要把lock传到函数的参数l #lock防止在屏幕上打印的时候会乱
lock
5.进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有以下几个主要方法:
- apply:从进程池里取一个进程并执行
- apply_async:apply的异步版本
- terminate:立刻关闭线程池
- join:主进程等待所有子进程执行完毕,必须在close或terminate之后
- close:等待所有进程结束后,才关闭线程池
from multiprocessing import Process, Pool
import time
import os def Foo(i):
time.sleep(2)
print("in process",os.getpid())
return i + 100 def Bar(arg):
print('-->exec done:', arg,os.getpid()) if __name__ == '__main__': #多进程,必须加这一句(windows系统)
pool = Pool(processes=3) #允许进程池同时放入3个进程
print("主进程",os.getpid()) for i in range(10):
pool.apply_async(func=Foo, args=(i,), callback=Bar) #callback=回调,执行完Foo(),接着执行Bar()
# pool.apply(func=Foo, args=(i,)) #串行 print('end')
pool.close()
pool.join() #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。必须先close(),再join()
Pool
协程
1.简介
协程(Coroutine) : 是单线程下的并发 , 又称微线程 , 纤程 . 协程是一种用户态的轻量级线程 , 即协程有用户自己控制调度
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态
使用协程的优缺点
优点 :
- 协程的切换开销更小 , 属于程序级别的切换 , 更加轻量级
- 单线程内就可以实现并发的效果 , 最大限度利用CPU
缺点 :
- 协程的本质是单线程下 , 无法利用多核 , 可以是一个程序开启多个进程 , 每个进程内开启多个线程 , 每个线程内开启协程
- 协程指的是单个线程 , 因而一旦协程出现阻塞 将会阻塞整个线程
2.Greenlet
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
手动切换
from greenlet import greenlet def test1():
print(12)
gr2.switch() #到这里切换到gr2,执行test2()
print(34)
gr2.switch() #切换到上次gr2运行的位置 def test2():
print(56)
gr1.switch() #切换到上次gr1运行的位置
print(78) gr1 = greenlet(test1) #启动一个协程gr1
gr2 = greenlet(test2) #启动一个协程gr2 gr1.switch() #开始运行gr1
greenlet
3.Gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。
(1)IO阻塞自动切换
import gevent def foo():
print('Running in foo')
gevent.sleep(2)
print('阻塞时间最长,最后运行') def bar():
print('running in bar')
gevent.sleep(1)
print('foo()还在阻塞,这里第二个运行') def func3():
print("running in func3 ")
gevent.sleep(0)
print("其它两个还在IO阻塞先运行") #创建协程实例
gevent.joinall([
gevent.spawn(foo), #生成,
gevent.spawn(bar),
gevent.spawn(func3),
]) #遇到IO自动切换 结果:
Running in foo
running in bar
running in func3
其它两个还在IO阻塞先运行
foo()还在阻塞,这里第二个运行
阻塞时间最长,最后运行 Process finished with exit code 0
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:
(2)爬虫例子:
from urllib import request
import gevent,time
from gevent import monkey
monkey.patch_all() #作用:把当前程序的所有的io操作给我单独的做上标记 def f(url):
print('GET: %s' % url)
resp = request.urlopen(url)
data = resp.read()
print('%d bytes received from %s.' % (len(data), url)) #同步需要的时间
urls = ['https://www.python.org/',
'https://www.yahoo.com/',
'https://github.com/' ]
time_start = time.time()
for url in urls:
f(url)
print("同步cost",time.time() - time_start) #下面是异步花费的时间
async_time_start = time.time()
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://github.com/'),
])
print("异步cost",time.time() - async_time_start) 结果:
GET: https://www.python.org/
48954 bytes received from https://www.python.org/.
GET: https://www.yahoo.com/
491871 bytes received from https://www.yahoo.com/.
GET: https://github.com/
51595 bytes received from https://github.com/.
同步cost 4.928282260894775
GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
48954 bytes received from https://www.python.org/.
494958 bytes received from https://www.yahoo.com/.
51599 bytes received from https://github.com/.
异步cost 1.4920852184295654
IO多路复用
详解:http://www.cnblogs.com/alex3714/articles/5876749.html
selectors模块
selectors基于select模块实现IO多路复用,调用语句selectors.DefaultSelector()
,特点是根据平台自动选择最佳IO多路复用机制,调用顺序:epoll > poll > select
做一个socket servers
import selectors
import socket
sel = selectors.DefaultSelector() # 根据平台自动选择最佳IO多路复用机制 def accept(sock, mask):
conn, addr = sock.accept() # Should be ready
# print('accepted', conn, 'from', addr,mask)
conn.setblocking(False) #设置为非阻塞IO
sel.register(conn, selectors.EVENT_READ, read)
#新连接注册read回调函数
#将conn和read函数注册到一起,当conn有变化时执行read函数 def read(conn, mask):
data = conn.recv(1024) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data) # Hope it won't block
else:
print('closing', conn)
sel.unregister(conn)
conn.close() sock = socket.socket()
sock.bind(('localhost', 9999))
sock.listen(100)
sock.setblocking(False) #设置为非阻塞IO
sel.register(sock, selectors.EVENT_READ, accept)
# 将sock和accept函数注册到一起,当sock有变化时执行accept函数 while True:
events = sel.select() #默认阻塞,有活动连接就返回活动的连接列表,监听[(key1,mask1),(key2),(mask2)] for key, mask in events:
callback = key.data #accept #1 key.data就是accept # 2 key.data就是read
callback(key.fileobj, mask) #key.fileobj= 文件句柄
# 1 key.fileobj就是sock # 2 key.fileobj就是conn
server
import socket
import sys messages = [ b'This is the message. ',
b'It will be sent ',
b'in parts.',
]
server_address = ('localhost', 9999) # Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(5)]
print(socks)
# Connect the socket to the port where the server is listening
print('connecting to %s port %s' % server_address)
for s in socks:
s.connect(server_address) for message in messages: # Send messages on both sockets
for s in socks:
print('%s: sending "%s"' % (s.getsockname(), message) )
s.send(message) # Read responses on both sockets
for s in socks:
data = s.recv(1024)
print( '%s: received "%s"' % (s.getsockname(), data) )
if not data:
print( 'closing socket', s.getsockname() )
mutlti conn socket client
12.python进程\协程\异步IO的更多相关文章
- python 多协程异步IO爬取网页加速3倍。
from urllib import request import gevent,time from gevent import monkey#该模块让当前程序所有io操作单独标记,进行异步操作. m ...
- Python 8 协程/异步IO
协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来 ...
- Python全栈开发-Day10-进程/协程/异步IO/IO多路复用
本节内容 多进程multiprocessing 进程间的通讯 协程 论事件驱动与异步IO Select\Poll\Epoll——IO多路复用 1.多进程multiprocessing Python ...
- Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动
1 Gevent 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...
- python之协程与IO操作
协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...
- 想使用gevent、mysql、sqlalchemy实现python项目协程异步达到并发的效果
如题,但是查看了很多资料,都说python这边的mysql不支持异步并发,只能阻塞进行,心塞30秒,暂时放弃这方面的研究 如果不操作数据库的化,比如请求url.操作文件,还是可以用gevent来异步实 ...
- 线程、进程、协程 异步io
https://www.cnblogs.com/wupeiqi/articles/6229292.html
- Python学习笔记整理总结【网络编程】【线程/进程/协程/IO多路模型/select/poll/epoll/selector】
一.socket(单链接) 1.socket:应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socke ...
- python第十周:进程、协程、IO多路复用
多进程(multiprocessing): 多进程的使用 multiprocessing是一个使用类似于线程模块的API支持产生进程的包. 多处理包提供本地和远程并发,通过使用子进程而不是线程有效地侧 ...
随机推荐
- And【sql语句之为何用and一个字段两个值得不到表中的数据】
一.[一个表的一个字段的多个条件用and连接] 用and是查不到值的, and是多个条件同时成立, 也就是一个字段是不能同时等于两个值的. '; 二[相同两个表的两个相同字段的查询用and连接] '; ...
- AspNet Core Api Restful +Swagger 实现微服务之旅 (三)
(1) 访问Rest ful接口时 Token验证 返回数据格式封装 (一)访问时Token验证 返回数据格式封装 1.1访问Api接口 方法 实现 1.1.1 创建访问Rest ...
- 具体解释Java虚拟机类载入
概述 在Java语言里面,类型的载入.连接和初始化过程都是在程序运行期间完毕的.虚拟机把描写叙述类的数据从Class文件或其他地方载入到内存,并对数据进行校验.转换解析和初始化,终于形成能够被虚拟机直 ...
- webpack+babel+transform-runtime, IE下提示Promise未定义?
知识要求 babel的基础知识(推荐阮一峰的babel入门教程) 充分理解babel-plugin-transform-runtime与babel-runtime的作用(推荐github项目首页) w ...
- 翻译:SET NAMES
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- Html 段落自动换行
1.段落换行 在 Html 中,关于段落换行,是我们经常遇见的问题,那么正如我下图没加换行代码所示: 在网页上的显示的样式,是这样的: 你可以看见,原本的样式,不会自动换行.在 div 中,加入一个样 ...
- django2.0+linux服务器 ,如何让自己电脑访问
这几天一直在搞这个服务器端口开放问题,来让自己电脑可以访问服务器下的django网页,今天终于弄好了~~~~~离成功又进了一步~~~~~ 1.首先,我们来开放一个linux服务器的端口(我开放了828 ...
- lua中怎么替换掉字符串中的$^特殊字符?
Lua 常规替换字符串如何替换 s = string.gsub("Lua is good", "good", "bad") print(s) ...
- ajaxFileUpload上传文件简单示例
写在前面: 上传文件的方式有很多,最近在做项目的时候,一开始也试用了利用jquery的插件ajaxFileUpload来上传大文件,下面,用一个上传文件的简单例子,记录下,学习的过程~~~ 还是老样子 ...
- 排序sort,统计wc
[root@localhost ~]# sort /etc/passwd 注释:默认按字母升序排 abrt:x::::/etc/abrt:/sbin/nologin adm:x:::adm:/var/ ...