協程----greenlet模塊,gevent模塊

1.協程初識,greenlet模塊java

2.gevent模塊(須要pip安裝)python

 

一.協程初識,greenlet模塊:nginx


協程:是單線程下的併發,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。數據庫

greenlet模塊要本身用pip安裝編程

#協程:
    #本質上是一個線程
    #可以在多個任務之間切換來節省一些IO時間
    #協程中任務之間的切換也消耗時間,可是開銷遠遠小於進程線程之間的切換

#協程的意義:
    #在遇到IO操做的時候,切換到另一個任務
    #規避以前任務的IO時間,來提升cpu的利用率
    #在實際工做中會採用:進程+線程+協程,來提升代碼的併發效果
    #進程是cpu核數+1,線程是cpu核數*5,每一個線程中協程最多能夠起500個
#好比:
    #發送了一個網頁請求後,在網絡延時,等待網頁響應的時間(等待IO),就能夠用協程去切換任務利用等待的時間,繼續發送多個網頁請求,從而提升效率
    #進程5,線程20,協程500 = 總共能夠有50000個協程:一臺4c的機器最多能夠接收的併發數
#數據庫,負載均衡,讓不少個請求,平均分攤給各個服務器
    #nginx組件 大型互聯網公司會用到,就是用來幫你分發任務的,併發最大承載量就是50000,用的就是協程機制
    #通常狀況下就是根據這個規則,上下浮動

#協程的調度是由gevent完成的,而進程和線程的調度是cpu完成的

#greenlet之後都不怎麼樣,協程仍是主要用gevent
#真正的協程模塊就是使用greenlet完成的切換
from greenlet import greenlet
def eat():
    print('eating')
    g2.switch()       #切換到g2運行g2,切記錄當前g1運行的位置,切換後,若是g2那沒切換回來,後面這句    print('eat end') 就不會運行了
    print('eat end')

def play():
    print('playing')
    g1.switch()
    print('play end')

g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()

# def play2():
#     print('playing')
#     g1.switch()
#     print('play end')
#
# def ch():
#     play2.__name__ = 'play3'
# ch()
# print(play2.__name__)
協程初識-greenlet模塊

#協程
#因爲cpython解釋器中的GIL緣由,致使python中多線程被弱化了,並且切換多個線程之間也要時間開銷
#因此就出現了協程,協程的切換效率更快,把1個線程的做用發揮到了極致,提升1個cpu的利用率。減小線程的時間開銷
#java裏也有協程,可是沒有這麼被重視json

 

二.gevent模塊服務器


安裝:pip3 install gevent網絡

Gevent 是一個第三方庫,能夠輕鬆經過gevent實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet所有運行在主程序操做系統進程的內部,但它們被協做式地調度。多線程

#切記 from gevent import monkey;monkey.patch_all()  這句話必定要寫在想更改的模塊前面併發

#GKX
#gevent模塊  join,joinall,value,spawn
#協程適用於網絡延遲的時候,也就是適合作爬蟲的時候 ,或者socket鏈接的時候。代碼若是沒有高IO,不必用協程
#gevent只會識別它認識的IO操做
from gevent import monkey;monkey.patch_all() #monkey這句話必定要寫在想更改的模塊前面
import time  #經過猴子補丁,讓sleep不阻塞
import gevent
import threading
# 用過gevent就會知道,會在最開頭的地方gevent.monkey.patch_all();把標準庫中的thread/socket等給替換掉.
# 這樣咱們在後面使用socket的時候能夠跟日常同樣使用,無需修改任何代碼,可是它變成非阻塞的了.

from greenlet import greenlet
def eat():
    print(threading.current_thread().getName())  #>>DummyThread-1  dummy(仿製品)
    print('eating')
    time.sleep(1)    #使用猴子補丁後,至關於 gevent.sleep
    # gevent.sleep(1)
    print('eat end')

def play():
    print(threading.current_thread().getName())
    print('playing')
    # time.sleep(1)
    gevent.sleep(1)
    print('play end')
g1 = gevent.spawn(eat)  #開啓一個協程, spawn(大量生產意思)
g2 = gevent.spawn(play)
g1.join() #讓線程等待協程的結果,若是沒有join,線程執行完畢後直接關閉,等不到協程的結果
g2.join()
print('11111111111111')
#遇到sleep後會非阻塞,先執行其餘任務,而後再同時來sleep1秒來打印任務
#有了gevent和猴子補丁,只要把函數註冊進 gevent模塊裏,就可使用協程了,其餘都不用管
#當遇到IO會自動幫你切換到其餘任務,最後再一塊兒共享,全部任務的IO操做

#遇到IO—執行其餘任務—其餘任務執行到也遇到了IO——繼續執行其餘任務—都遇到了IO—在IO之間來回切換,看誰解除了IO,立刻繼續運行
#從而把等待IO的時間,用來執行任務。而後共享IO時間,達到提升效率的目的
#咱們能夠不用理會gevent的執行過程,註冊完線程運行等待結果就行了
gevent模塊-例子和解釋
from gevent import monkey;monkey.patch_all() #monkey這句話必定要寫在想更改的模塊前面
import time  #經過猴子補丁,讓sleep不阻塞
import gevent
import threading
#同步和異步
def task():
    time.sleep(0.5)
    print('12345')

def sync():
    for i in range(10):
        task()

def a_sync():
    g_lst = []
    for i in range(10):
        g = gevent.spawn(task)
        g_lst.append(g)
    gevent.joinall(g_lst) # == for g in g_lst:g.join()

sync()
a_sync()
同步和異步的比較
# monkey patch指的是在運行時動態替換,通常是在startup的時候.
# 用過gevent就會知道,會在最開頭的地方gevent.monkey.patch_all();把標準庫中的thread/socket等給替換掉.
# 這樣咱們在後面使用socket的時候能夠跟日常同樣使用,無需修改任何代碼,可是它變成非阻塞的了.
# 以前作的一個遊戲服務器,不少地方用的import json,後來發現ujson比自帶json快了N倍,
# 因而問題來了,難道幾十個文件要一個個把import json改爲import ujson as json嗎?
# 其實只須要在進程startup的地方monkey patch就好了.是影響整個進程空間的.同一進程空間中一個module只會被運行一次.
# 下面是代碼:

# import json
# import ujson
#
# def monkey_patch_json():
#     json.__name__ = 'ujson'
#     json.dumps = ujson.dumps
#     json.loads = ujson.loads
#
# monkey_patch_json()
#
# print
# 'main.py', json.__name__
# import sub
#
#
# import json
# print 'sub.py',json.__name__

# 最後,注意不能單純的json = ujson來替換.
猴子補丁

 

用gevent模塊實現的socket服務端的併發:

from gevent import monkey;monkey.patch_all()

from gevent import monkey;monkey.patch_all()
import gevent


def func(conn):
    conn.send(b'hello')
    msg = conn.recv(1024).decode('utf8')
    print(msg)
    conn.close()  #關閉若是放在循環裏,就立刻關閉了,沒有鏈接的意義

while True:
    conn, addr = sk.accept()  #在這裏阻塞,一直監聽,一旦有client鏈接,立刻把conn扔給一個併發。而後繼續循環,阻塞
    g = gevent.spawn(func,conn)

sk.close()
server端
import time
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))

print(sk.recv(1024))
msg = input('>>>> ').encode('utf8')
sk.send(msg)
sk.close()
client端
相關文章
相關標籤/搜索