協作式go程

2021-09-16 23:46:01 字數 1410 閱讀 7226

考慮如下開發框架,一組網路接收goroutine接收網路包,解包,然後將邏輯包推送到訊息佇列,由乙個單一的邏輯處理goroutine負責從佇列中提取邏輯包並處理(這樣主處理邏輯中基本上不用考慮多執行緒競爭的鎖問題了)。

如果邏輯包的處理涉及到呼叫可能會阻塞的函式呼叫怎麼辦,如果在處理函式中直接呼叫這樣的函式將導致邏輯處理goroutine被阻塞,無法繼續處理佇列中被排隊的資料報,這將嚴重降低服務的處理能力。

為了實現這個目的,我需要乙個類似lua的單執行緒協作式coroutine排程機制,單執行緒讓使用者不用擔心資料競爭,協作式可以讓coroutine在執行非同步呼叫前將執行權交出去,等非同步結果返回後再將執行權切換回來,線性的執行後續**。

但是,goroutine天生就是多執行緒排程執行的,有辦法實現這個目標嗎?答案是肯定的。

我們可以實現乙個邏輯上的單執行緒,從全域性上看,只有唯一乙個goroutine可以執行邏輯處理**。核心思想就是由排程器從任務佇列中提取任務,挑選乙個空閒的goroutine,將其喚醒並讓自己阻塞,當goroutine需要阻塞時就喚醒排程器並將自己阻塞。這樣全域性上就只有唯一的goroutine在執行邏輯**。

下面是乙個使用示例:

package main

import (

"fmt"

"time"

"coop-go"

)func main() )

//呼叫阻塞函式

p.call(func () )

//繼續投遞任務

p.postevent(1)

})for i := 0; i < 10000; i++

p.start()

fmt.printf("scheduler stop,total taskcount:%d\n",c2)

}

首先用乙個任務處理函式作為引數建立排程器。然後向排程器投遞任務觸發處理迴圈,最後啟動處理。

這裡唯一需要關注的是call,它的引數是乙個函式閉包,call將會在並行的環境下執行傳給它的閉包(不釋放自己執行權的同時喚醒排程器去排程其它任務),因為這個閉包是並行執行的,所以閉包內不能含有任何非

執行緒安全的**,可以將同步的阻塞呼叫放到閉包中,不用擔心阻塞主處理邏輯。call內部在閉包呼叫返回之後會將自己阻塞並新增到喚醒佇列中等待排程器排程執行。獲得執行權之後才從call呼叫返回,從call返回

之後,又回到執行緒安全的執行環境下。

下面是乙個同步獲取redis資料的呼叫:

ret

call(func() )

if ret

在我的 i5 雙核 2.5ghz mac mini上每秒鐘可以執行100w次的排程,雖然跟c協程數千萬的排程次數沒法比,但是也基本夠用了,畢竟在實現的使用中,每秒能處理10w的請求已經相當不錯了。

更多使用示例請見coop-go-exampe

go 協程等待

sync包提供了基本的同步基元,如互斥鎖 但是這裡不是討論執行緒通訊的問題 而執行緒通訊應使用channel 以前使用time.sleep 來保證執行緒執行完成,顯然執行緒執行所需要的時間不確定 sync裡面有乙個waitgroup,它是乙個結構體,可以用於等待執行緒執行 這樣不用去估算執行緒需要執...

Go 協程 通道

目錄 go 協程 go 通道 go 協程go 協程可以看作是輕量級執行緒。與執行緒相比,建立乙個go協程的成本很小。因此在go應用中,常常會看到有數以千計的go協程併發地執行 go 協程相比於執行緒的優勢 啟動乙個go協程 在呼叫函式和方法時,在前面加上關鍵字go,可以讓乙個新的go協程併發執行 p...

GO總結六協程

比執行緒更加輕量級的並行方法。go從語言層次就支援了這一操作 使用起來很簡單,就只用關鍵字go標識一下,它就自動開乙個協程去執行了 package main import fmt time func show func main 結果 注意,主協程退出後,子協程也會退出 讓出時間片,其他協程先執行 ...