Golang 深入理解io.Pipe同步通道

io.Pipe實現了一對多、多對多、多對一的內存數據通道功能,創建方式如下

func Pipe() (*PipeReader, *PipeWriter)

Pipe creates a synchronous in-memory pipe. It can be used to connect code expecting an io.Reader with code expecting an io.Writer.
Reads and Writes on the pipe are matched one to one except when multiple Reads are needed to consume a single Write. That is, each Write to the PipeWriter blocks until it has satisfied one or more Reads from the PipeReader that fully consume the written data. The data is copied directly from the Write to the corresponding Read (or Reads); there is no internal buffering.
It is safe to call Read and Write in parallel with each other or with Close. Parallel calls to Read and parallel calls to Write are also safe: the individual calls will be gated sequentially.

官方說明中重點提到不管是並行寫入還是並行讀取都是安全的。其次是pipe適用於多個讀取按寫入順序消費單個寫入數據的場景。每次寫入數據後都將被阻塞直到數據被完整讀取。最後是數據拷貝過程沒有使用緩存。這看起來其實挺晦澀的,要真正理解還是得有具體場景。

io.Pipe類似餐廳點餐。雖然有多人點餐但點單系統會按照點餐順序一道菜一道菜發送到廚房,且每次發送都是上一道菜完成之後。那麼點單系統就是PipeWriter,每一次發送就是Write,爲了方便理解對源代碼進行了簡化如下:

func (p *pipe) Write(b []byte) (n int, err error) {
    //寫入鎖,每次數據完整寫入前其他並行寫入將在此被阻塞
    p.wrMu.Lock()
    defer p.wrMu.Unlock()

    //循環寫入直到完整寫入
    for len(b) > 0 {
        //發送數據到p.wrCh,讀取端將從p.wrCh中讀取
        p.wrCh <- b
        //讀取端單次讀取的量
        nw := <-p.rdCh
        b = b[nw:]
        n += nw 
    }
    return n, nil
}

現在訂單(魚香茄子一份)進入廚房,廚師有好幾個,每一個廚師就是PipeReader。第一個廚師看到(Read)”魚”字,開始準備魚。第二廚師看到”茄子”開始準備茄子。第三個廚師看到“一份”開始洗鍋熱鍋。簡化代碼如下:

func (p *pipe) Read(b []byte) (n int, err error) {
    //接收p.wrCh中數據
    bw := <-p.wrCh
    //不完整讀取量
    nr := copy(b, bw)   
    //讀取量發送給寫入端
    p.rdCh <- nr
    return nr, nil  
}

到此廚師不斷處理訂單,一道道菜按順序完成。雖然官方說明中提到Pipe是一對多的通道。但從源碼中可以看到寫入是阻塞的,多對多的關係依然不會出現問題。當然需要注意多個讀取存在拆分包的情況。也就是說每次寫入都是一個不可分割的數據包時,那麼多個讀取雖然是線程安全的,但也會發生讀取的數據不完整的情況。比如“魚香茄子”只讀取了魚。要避免這種情況,要麼是讀取的緩存夠大,要麼就是多對一的方式,只有一個讀取端。

最後,Pipe允許在並行的情況下關閉寫入或讀取。當任意一個寫入或讀取端調用Close函數後,其他線程都將立即返回已操作數據量和CloseError標記。這是非常大的一個優點。因爲寫入的是一個大數據塊,分多次讀取的中途被關閉。不管是寫入端還是讀取端都能獲取到已處理的數據量。可以防止數據的丟失。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章