1. 程式人生 > >python進程和線程3

python進程和線程3

停止工作 結果 form selector not deamon io模型 系統調用 socket

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. 1. 阻塞 IO

技術分享

  1. 1. 非阻塞 IO

非阻塞IO:發送多次系統調用

優點:wait for data無阻塞

缺點:系統調用太多

不能及時拿到數據

兩個階段:wait for data非阻塞

copy data 阻塞

技術分享

非阻塞的recvform系統調用調用之後,進程並沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個error。進程在返回之後,可以幹點別的事情,然後再發起recvform系統調用。重復上面的過程,循環往復的進行recvform系統調用。這個過程通常被稱之為輪詢。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。需要註意,拷貝數據整個過程,進程仍然是屬於阻塞的狀態。

  1. 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. 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