waitgroup 和通道(channel)是常見的 2 種併發控制的方式。
如果併發啟動了多個子協程,需要等待所有的子協程完成任務,waitgroup 非常適合於這類場景,例如下面的例子:
1234567
891011
1213
1415
16
var wg sync.waitgroupfunc dotask(n int)
func main()
wg.wait()
fmt.println("all task done")
}
wg.wait()
會等待所有的子協程任務全部完成,所有子協程結束後,才會執行wg.wait()
後面的**。
1234
task 3 donetask 1 done
task 2 done
all task done
waitgroup 只是傻傻地等待子協程結束,但是並不能主動通知子協程退出。假如開啟了乙個定時輪詢的子協程,有沒有什麼辦法,通知該子協程退出呢?這種場景下,可以使用select+chan
的機制。
1234567
891011
1213
1415
1617
1819
2021
22
var stop chan boolfunc reqtask(name string) }}
func main()
子協程使用 for 迴圈定時輪詢,如果stop
通道有值,則退出,否則繼續輪詢。
1234
worker1 send requestworker1 send request
worker1 send request
stop worker1
更複雜的場景如何做併發控制呢?比如子協程中開啟了新的子協程,或者需要同時控制多個子協程。這種場景下,select+chan
的方式就顯得力不從心了。
go 語言提供了 context 標準庫可以解決這類場景的問題,context 的作用和它的名字很像,上下文,即子協程的下上文。context 有兩個主要的功能:
context.withcancel()
建立可取消的 context 物件,即可以主動通知子協程退出。
使用 context 改寫上述的例子,效果與select+chan
相同。
1234567
891011
1213
1415
1617
1819
20
func reqtask(ctx context.context, name string) }}func main()
1234567
8910
func main()
為每個子協程傳遞相同的上下文ctx
即可,呼叫cancel()
函式後該 context 控制的所有子協程都會退出。
1234567
8
worker1 send requestworker2 send request
worker1 send request
worker2 send request
worker1 send request
worker2 send request
stop worker1
stop worker2
如果需要往子協程中傳遞引數,可以使用context.withvalue()
。
1234567
891011
1213
1415
1617
1819
2021
2223
2425
2627
type options structfunc reqtask(ctx context.context, name string) }}
func main() )
go reqtask(vctx, "worker1")
go reqtask(vctx, "worker2")
time.sleep(3 * time.second)
cancel()
time.sleep(3 * time.second)
}
如果需要控制子協程的執行時間,可以使用context.withtimeout
建立具有超時通知機制的 context 物件。
1234567
8910
func main()
withtimeout()
的使用與withcancel()
類似,多了乙個引數,用於設定超時時間。執行結果如下:
1234567
worker2 send requestworker1 send request
worker1 send request
worker2 send request
stop worker2
stop worker1
before cancel
因為超時時間設定為 2s,但是 main 函式中,3s 後才會呼叫cancel()
,因此,在呼叫cancel()
函式前,子協程因為超時已經退出了。
超時退出可以控制子協程的最長執行時間,那context.withdeadline()
則可以控制子協程的最遲退出時間。
1234567
891011
1213
1415
1617
1819
2021
2223
func reqtask(ctx context.context, name string) }}func main()
執行結果如下:
12345
worker2 send requestworker1 send request
stop worker2 context deadline exceeded
stop worker1 context deadline exceeded
before cancel
可以看到,子協程worker1
和worker2
均是因為截止時間到了而退出。
Java併發程式設計教程
1 使用執行緒的經驗 設定名稱 響應中斷 使用threadlocal 2 executor executorservice和future 3 阻塞佇列 put和take offer和poll drainto 4 執行緒間的協調手段 lock condition wait notify notifya...
Java併發程式設計教程
多執行緒程式包含兩個或多個可同時執行的部分,每個部分可以同時處理不同的任務,從而能更好地利用可用資源,特別是當您的計算機有多個cpu時。多執行緒使您能夠寫入多個活動,可以在同一程式中同時進行操作處理。新執行緒 new 新執行緒在新的狀態下開始其生命週期。直到程式啟動執行緒為止,它保持在這種狀態。它也...
併發程式設計學習 併發程式設計的挑戰
死鎖 資源限制的挑戰 併發程式設計的目的是為了讓程式執行的更快,但是並不是啟動更多的執行緒,就能讓程式最大限度的併發執行。在進行併發程式設計時,如果希望通過多執行緒執行任務讓程式執行的更快,會面臨非常多的挑戰,比如上下文切換的問題,死鎖的問題,以及受限於硬體和軟體的資源限制問題 即使是單核處理器也支...