python進程和線程3
1 multiprocessing模塊
(1.)直接導入 from multiprocessing import Process import os import time def info(name): print("name:",name) print(‘parent process:‘, os.getppid()) print(‘process id:‘, os.getpid()) print("------------------") def foo(name): info(name) time.sleep(50) if __name__ == ‘__main__‘: info(‘main process line‘) p1 = Process(target=info, args=(‘alvin‘,)) p2 = Process(target=foo, args=(‘egon‘,)) p1.start() p2.start() p1.join() p2.join() print("ending") time.sleep(100) >> name: main process line parent process: 16976 process id: 18456 ------------------ name: alvin parent process: 18456 process id: 19884 ------------------ name: egon parent process: 18456 process id: 19112 ------------------ ending
(2.)創建類的方法
構造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
group: 線程組,目前還沒有實現,庫引用中提示必須是None;
target: 要執行的方法;
name: 進程名;
args/kwargs: 要傳入方法的參數。
實例方法:
is_alive():返回進程是否在運行。
join([timeout]):阻塞當前上下文環境的進程程,直到調用此方法的進程終止或到達指定的timeout(可選參數)。
start():進程準備就緒,等待CPU調度
run():strat()調用run方法,如果實例進程時未制定傳入target,這star執行t默認run()方法。
terminate():不管任務是否完成,立即停止工作進程
屬性:
daemon:和線程的setDeamon功能一樣
name:進程名字。
pid:進程號。
2 協程
協程的優點:
(1) 由於單線程不存在切換
(2) 不再有任何鎖的概念
yield是最基本的攜程函數 沒有辦法監聽到IO,進行切換 可以保存到數據的狀態通過send方法來運行 import time # 註意到consumer函數是一個generator(生成器): # 任何包含yield關鍵字的函數都會自動成為生成器(generator)對象 def consumer(): r = ‘‘ while True: n = yield r if not n: return print(‘[CONSUMER] ←← Consuming %s...‘ % n) time.sleep(1) r = ‘200 OK‘ def produce(c): # 1、首先調用c.next()啟動生成器 next(c) n = 0 while n < 5: n = n + 1 print(‘[PRODUCER] →→ Producing %s...‘ % n) # 2、然後,一旦生產了東西,通過c.send(n)切換到consumer執行; cr = c.send(n) # 4、produce拿到consumer處理的結果,繼續生產下一條消息; print(‘[PRODUCER] Consumer return: %s‘ % cr) # 5、produce決定不生產了,通過c.close()關閉consumer,整個過程結束。 c.close() if __name__==‘__main__‘: # 6、整個流程無鎖,由一個線程執行,produce和consumer協作完成任務,所以稱為“協程”,而非線程的搶占式多任務。 c = consumer() produce(c)
greenlet模塊
可以實現手動切換
調用屬性swich
gevent可以實現IO的監聽
gevent.joinall 開啟所有程序
gevent.spawn 切換
3 IO模型
IO指input, output
IO發生時涉及的對象和步驟
會涉及到兩個系統對象,一個是調用這個IO的process(or thread),另一個就是系統內核(kernel)。當一個操作發生時,會經歷兩個階段:
(1) 等待數據準備
(2) 將數據從內核拷貝到進程中
IO模型類型:
- 1. 阻塞 IO
- 1. 非阻塞 IO
非阻塞IO:發送多次系統調用
優點:wait for data無阻塞
缺點:系統調用太多
不能及時拿到數據
兩個階段:wait for data非阻塞
copy data 阻塞
非阻塞的recvform系統調用調用之後,進程並沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個error。進程在返回之後,可以幹點別的事情,然後再發起recvform系統調用。重復上面的過程,循環往復的進行recvform系統調用。這個過程通常被稱之為輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。需要註意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。
- 1. IO多路復用(監聽多個鏈接)
特點:(1)全程阻塞
能監聽多個文件描述符 實現並發
#服務端 import select import socket sock=socket.socket()#產生一個套接字 sock.bind(("127.0.0.1",8080)) sock.listen(5) sock.setblocking(False) inputs=[sock,] while 1: r,w,e=select.select(inputs,[],[])#監聽有變化的套接字sock #wait for data for obj in r: if obj==sock: conn,addr=obj.accept()#從內核copy信息到用戶態 print("conn",conn) inputs.append(conn)#監聽列表添加客戶conn else: data=obj.recv(1024)#接收信息 print(data.decode("utf8")) send_data=input(">>")#發送信息 obj.send(send_data.encode("utf8")) #客戶端 import socket sock=socket.socket() sock.connect(("127.0.0.1",8080)) while 1: data=input("input>>") sock.send(data.encode("utf8")) recv_data=sock.recv(1024) print(recv_data.decode("utf8")) sock.close()
對於文件描述符(套接字對象)
(1) 是一個非零整數,不會變
(2) 收發數據的時候,對於接收端而言,數據先到內核空間,然後copy到用戶空間,同時,內核空間數據清除
- 1. 異步IO
全程無阻塞
5.驅動信號
小結:
有阻塞blocking
無阻塞non-blocking
調用blocking IO會一直block住對應的進程知道操作完成
non-blocking IO在kernel還準備數據的情況下會立刻返回
有阻塞是同步阻塞:阻塞 非阻塞 IO多路復用
無阻塞是異步阻塞:異步IO
4 selectors模塊
IO多路復用實現機制
Win:select
Linux:select,poll,epoll
Select缺點:1.每次調用select都要將所有的fd(文件描述符)拷貝到內核空間,導致效率下降
2.遍歷所有的fd,是否有數據訪問(最重要的問題)
3.最大連接數(1024)
poll:最大連接數沒有限制
epoll:1.第一個函數創建epoll句柄,將所有的fd(文件描述符)拷貝到內核空間
只需要拷貝一次
2.回調函數:某一個函數或者某一個動作成功完成之後會觸發的函數
為所有的fd綁定一個回調函數,但有數據訪問觸發該回調函數
回調函數將fd放到列表中
import selectors import socket sock=socket.socket() sock.bind(("127.0.0.1",8080)) sock.listen(5) sock.setblocking(False) sel=selectors.DefaultSelector()#根據具體平臺選擇最佳IO多路機制 def read(conn,mask): try: data=conn.recv(1024) print(data.decode("utf8")) data2=input(">>") conn.send(data2.encode("utf8")) except Exception: sel.unregister(conn) def accept(sock,mask): sel.register(sock,selectors.EVENT_READ,accept) conn,addr=sock.accept() sel.register(conn,selectors.EVENT_READ,read) sel.register(sock,selectors.EVENT_READ,accept)#註冊功能 while 1: events=sel.select() for key,mask in events: print(key.data)#定義的函數 print(key.fileobj)#socket對象 func=key.data obj=key.fileobj func(obj,mask) break import socket sock=socket.socket() sock.connect(("127.0.0.1",8080)) while 1: data=input("input>>") sock.send(data.encode("utf8")) recv_data=sock.recv(1024) print(recv_data.decode("utf8")) sock.close()
5. 隊列
隊列用在多線程,多進程中,用來保護數據
隊列是個數據類型
優點:線程安全
import queue q=queue.Queue(3)#默認是先進先出 q.put(111) q.put("hello") q.put(222) print(q.get()) print(q.get()) print(q.get()) >> 111 hello 222 import queue q=queue.Queue(3)#默認是先進先出 q.put(111) q.put("hello") q.put(222) q.put(223,False)#q=queue.Queue(3)隊列定義只能放3個值, # #超過限額時,返回錯誤信息 print(q.get()) print(q.get()) print(q.get()) q.get()#沒有數據的時候不會報錯,只會等待 q.get(False)#數據為空,報錯 先進後出 import queue q=queue.LifoQueue() q.put(111) q.put(5) q.put(43) print(q.get()) 優先級 import queue q=queue.PriorityQueue() q.put([4,"hello"]) q.put([1,"hello5"]) print(q.get())
python\進程和線程3