4.28 筆記

昨日內容回顧

  • 死鎖
'''
即使你知道如何搶鎖釋放鎖    也很可能形成程序的死鎖現象
後續咱們在寫項目的時候    也不會本身去處理鎖的問題    都是底層封裝好的   因此你不用擔憂
'''
  • 遞歸鎖
# 他也是一把互斥鎖,可是他能夠被第一個搶到它的人連續的acquire和release
每一次acquire內部都有一個引用計數加一
每一次release內部都有一個引用計算減一
只要引用計數不爲0   永遠也沒法被其餘人搶到
  • 信號量
#信號量在不一樣的領域和知識階段可能對應不一樣的概念
若是將互斥鎖比喻成一個廁所,那麼信號量就至關於多個廁所
  • event事件
一些線程/進程等待另外一些線程/進程發送能夠運行的信號   纔開始運行
from threading import Event
e = Event()

#等待
e.wait()
#發送信號
e.set()
  • 各類隊列
常見隊列   queue
先進先出
q = queue.Queue()
q.put()
q.get(timeout=3)
q.get_nowait()
q.full()
q.empty()
後進先出  LifoQueue()
q = queue.LifoQueue()
q.put()
q.get()
優先級	PriorityQueue()
q = queue.PriorityQueue()
q.put((10,"data"))
元組裏面的第一個參數是數字,而且支持負數
數字越小優先級越高
  • 進程池線程池
硬件的發展確定是趕不上軟件的開發速度
思考    咱們之前藉助於開設進程和線程的方式來實現TCP服務端的併發     每來一個客戶端就開設一個進程或線程

不管是開設進程仍是開設線程其實都會消耗必定的資源
咱們應該再保證計算機硬件安全的狀況下,最大限度的利用計算機

池的概念
    它的出現是爲了保證計算機硬件的安全
    下降了程序的運行效率   可是保證了計算機硬件安全
# 進程池線程池都不須要咱們本身去造,直接使用封裝好的模塊
from concurrent.future import ThreadPoolExecutor,ProcessPoolExecutor
# 生成進程池線程池
pool1 = ThreadPoolExecutor()    # 不默認是cpu個數的五倍
pool2 = ProcessPoolExecutor()    # 不默認是cpu個數
# 朝c池子中提交任務
pool1.submit(task,args...)    # 異步提交
res.result()   # 同步
# 池子對象的方法
pool1.shotdown()    # 關閉池子   等待池子中全部的任務運行結果   在繼續日後執行代碼

# 異步回調機制
給每個異步提交的任務綁定一個方法,一旦任務有結果了會自動觸發該方法
pool1.submit(task,arge).add_done_callback(call_back)
#注意異步回調機制函數拿到的也是一個對象
  • 協程
單線程下實現併發
這個概念徹底是咱們程序員本身想出來的

多道技術
     切換+ 保存狀態
咱們想經過代碼層面本身檢測IO行爲,一旦有IO代碼層面實現切換    這樣給操做系統的感受好像我這個程序一直運行沒有IO
欺騙操做系統從而最大化的利用cpu

一味的切換加保存狀態也有可能會下降程序的效率
計算密集型    不行
io密集型     能夠
  • gevent模塊
# 該模塊可以幫咱們檢測IO並實現切換
# from gevent import monkey;monkey.patch_all()
from gevent import spawn

# spawn 在檢測得時候   是異步提交的
spawn(server).join()
g = spawn(server)
q.join
  • 基於協程實現tcp服務端單線程下的併發
  • 總結
    • 多線程下面開設多線程,多線程下面利用協程,最大長度的提高軟件的運行效率

IO模型簡介

"""
咱們這裏研究的IO模型都是針對網絡IO的
Stevens在文章中一共比較了五種IO Model:
    * blocking IO           阻塞IO
    * nonblocking IO      非阻塞IO
    * IO multiplexing      IO多路複用
    * signal driven IO     信號驅動IO
    * asynchronous IO    異步IO
    由signal driven IO(信號驅動IO)在實際中並不經常使用,因此主要介紹其他四種IO Model。
"""
#1)等待數據準備 (Waiting for the data to be ready)
#2)將數據從內核拷貝到進程中(Copying the data from the kernel to the process)

同步異步
阻塞非阻塞
常見的網絡阻塞狀態:
  	accept
    recv
    recvfrom
    
    send雖然它也有io行爲 可是不在咱們的考慮範圍

阻塞IO模型

"""
咱們以前寫的都是阻塞IO模型  協程除外
"""
import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)


while True:
    conn, addr = server.accept()
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:break
            print(data)
            conn.send(data.upper())
        except ConnectionResetError as e:
            break
    conn.close()
    
# 在服務端開設多進程或者多線程 進程池線程池 其實仍是沒有解決IO問題	
該等的地方仍是得等 沒有規避
只不過多我的等待的彼此互不干擾

非阻塞IO

"""
要本身實現一個非阻塞IO模型
"""
import socket
import time


server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)
server.setblocking(False)
# 將全部的網絡阻塞變爲非阻塞
r_list = []
del_list = []
while True:
    try:
        conn, addr = server.accept()
        r_list.append(conn)
    except BlockingIOError:
        # time.sleep(0.1)
        # print('列表的長度:',len(r_list))
        # print('作其餘事')
        for conn in r_list:
            try:
                data = conn.recv(1024)  # 沒有消息 報錯
                if len(data) == 0:  # 客戶端斷開連接
                    conn.close()  # 關閉conn
                    # 將無用的conn從r_list刪除
                    del_list.append(conn)
                    continue
                conn.send(data.upper())
            except BlockingIOError:
                continue
            except ConnectionResetError:
                conn.close()
                del_list.append(conn)
        # 揮手無用的連接
        for conn in del_list:
            r_list.remove(conn)
        del_list.clear()

# 客戶端
import socket


client = socket.socket()
client.connect(('127.0.0.1',8081))


while True:
    client.send(b'hello world')
    data = client.recv(1024)
    print(data)

總結python

"""
雖然非阻塞IO給你的感受很是的牛逼
可是該模型會	長時間佔用着CPU而且不幹活 讓CPU不停的空轉
咱們實際應用中也不會考慮使用非阻塞IO模型

任何的技術點都有它存在的意義 
實際應用或者是思想借鑑
"""

IO多路複用

"""
當監管的對象只有一個的時候 其實IO多路複用連阻塞IO都比比不上!!!
可是IO多路複用能夠一次性監管不少個對象

server = socket.socket()
conn,addr = server.accept()

監管機制是操做系統自己就有的 若是你想要用該監管機制(select)
須要你導入對應的select模塊
"""
import socket
import select


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)
read_list = [server]


while True:
    r_list, w_list, x_list = select.select(read_list, [], [])
    """
    幫你監管
    一旦有人來了 馬上給你返回對應的監管對象
    """
    # print(res)  # ([<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080)>], [], [])
    # print(server)
    # print(r_list)
    for i in r_list:  #
        """針對不一樣的對象作不一樣的處理"""
        if i is server:
            conn, addr = i.accept()
            # 也應該添加到監管的隊列中
            read_list.append(conn)
        else:
            res = i.recv(1024)
            if len(res) == 0:
                i.close()
                # 將無效的監管對象 移除
                read_list.remove(i)
                continue
            print(res)
            i.send(b'heiheiheiheihei')

 # 客戶端
import socket


client = socket.socket()
client.connect(('127.0.0.1',8080))


while True:

    client.send(b'hello world')
    data = client.recv(1024)
    print(data)

總結linux

"""
監管機制其實有不少
select機制  windows linux都有

poll機制    只在linux有   poll和select均可以監管多個對象 可是poll監管的數量更多

上述select和poll機制其實都不是很完美 當監管的對象特別多的時候
可能會出現 極其大的延時響應

epoll機制   只在linux有
	它給每個監管對象都綁定一個回調機制
	一旦有響應 回調機制馬上發起提醒

針對不一樣的操做系統還須要考慮不一樣檢測機制 書寫代碼太多繁瑣
有一我的可以根據你跑的平臺的不一樣自動幫你選擇對應的監管機制
selectors模塊
"""

異步IO

"""
異步IO模型是全部模型中效率最高的 也是使用最普遍的
相關的模塊和框架
	模塊:asyncio模塊
	異步框架:sanic tronado twisted
		速度快!!!
"""
import threading
import asyncio


@asyncio.coroutine
def hello():
    print('hello world %s'%threading.current_thread())
    yield from asyncio.sleep(1)  # 換成真正的IO操做
    print('hello world %s' % threading.current_thread())


loop = asyncio.get_event_loop()
tasks = [hello(),hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

四個IO模型對比

參考博客園圖解,稍微瞭解便可程序員

相關文章
相關標籤/搜索