協程異步非阻塞

一、gevent. 在遇到io操做時會發生切換,切換gevent.joinall()中的gevent.spawn(a)去執行。python

使用非gevent封裝的sleep()時會發生阻塞服務器

import gevent
import time

def a():
    print("begin a",time.time())
    time.sleep(1)
    print("end a ",time.time())

def b():
    print("begin b",time.time())
    time.sleep(1)
    print("end b ",time.time())

def c():
    print("begin c",time.time())
    time.sleep(1)
    print("end c ",time.time())


if __name__ == "__main__":
    gevent.joinall([
        gevent.spawn(a),
        gevent.spawn(b),
        gevent.spawn(c),]
    )

運行結果:
begin a 1466328299.3592122
end a  1466328300.3595576
begin b 1466328300.3595576
end b  1466328301.3598628
begin c 1466328301.3598628
end c  1466328302.3601925

二、使用gevent.sleep(1)時是非阻塞的。可是固然這個sleep(1)睡眠1秒仍是要經歷的,只是a在睡眠時能夠切換到b或c去執行:socket

將上面的time.sleep(1)改成gevent.sleep(1)spa

gevent.sleep(1)

運行結果:
begin a 1466328564.0706365
begin b 1466328564.0891385
begin c 1466328564.089639
end a  1466328565.0892923
end c  1466328565.0907917
end b  1466328565.0907917

 

三、在文件最開始使用from gevent import monkey;monkey.patch_all() 能夠使time.sleep(1)爲非阻塞。這樣就不用gevent的sleep(1)了。線程

 

四、gevent實現單線程socket容許同時多個鏈接同時發生數據。  也能夠使用from gevent import socket,這樣就不用使用monkey.patch_all()了。code

簡單示例代碼:server

# 服務器端
import gevent
import time
import socket
from gevent import monkey;monkey.patch_all()

def server(port):
    s = socket.socket()
    s.bind(('127.0.0.1',port))
    s.listen(50)
    # s.setblocking(False)
    while True:
        conn, addr = s.accept()    # 接收客戶端鏈接
        print("來了一個鏈接:",addr)
        gevent.spawn(handle_switch,conn)   # gevent.spawn()裏的handle_switch()內部會非阻塞狀態

def handle_switch(conn):
    while True:
        try:
            data = conn.recv(1024)        # 非阻塞,並不表明會往下執行,在這裏而是發生切換。
        except ConnectionResetError as e:
            print(e)
            conn.close()
            break
        conn.send(data)                   # 非阻塞
        print("recv:",data.decode())
        if not data:
            conn.close()
            print("客戶端正常關閉")
            break
if __name__ == "__main__":
    server(9001)

# 客戶端
import socket

s = socket.socket()
s.connect(('127.0.0.1',9001))

while True:
    data = input(">> ").strip()
    if data == "":
        continue
    if data == "q":
        s.close()
        break
    s.send(bytes(data,encoding="utf-8"))
    data = s.recv(1024)
    print("recv:",data.decode())

發起兩個鏈接:blog

容許結果:進程

來了一個鏈接: ('127.0.0.1', 55801)    # 第一個客戶端鏈接
recv: asd                                         
recv: asd
來了一個鏈接: ('127.0.0.1', 55804)    # 第二個客戶端鏈接
recv: sdfa
recv: asdg
recv: 1
recv: 1
recv: 2
recv: 2
recv: 1
recv: 2
recv:                                                # 客戶端1斷開鏈接,data爲空
客戶端正常關閉                                   # 客戶端1正常斷開,即客戶端是s.close()的
recv: haha
recv:                                                # 客戶端2斷開鏈接,data爲空
客戶端正常關閉                                   # 客戶端1正常斷開,即客戶端是s.close()的
來了一個鏈接: ('127.0.0.1', 55820)     # 第三個客戶端鏈接
recv: asd
[WinError 10054] 遠程主機強迫關閉了一個現有的鏈接。       # 客戶端3非正常鏈接,應該是被kill掉了進程,客戶端發送rst包。
相關文章
相關標籤/搜索