Go Context 併發程式設計教程

2021-10-23 14:06:27 字數 3562 閱讀 4551

waitgroup 和通道(channel)是常見的 2 種併發控制的方式。

如果併發啟動了多個子協程,需要等待所有的子協程完成任務,waitgroup 非常適合於這類場景,例如下面的例子:

123

4567

891011

1213

1415

16

var wg sync.waitgroup

func dotask(n int)

func main()

wg.wait()

fmt.println("all task done")

}

wg.wait()會等待所有的子協程任務全部完成,所有子協程結束後,才會執行wg.wait()後面的**。

123

4

task 3 done

task 1 done

task 2 done

all task done

waitgroup 只是傻傻地等待子協程結束,但是並不能主動通知子協程退出。假如開啟了乙個定時輪詢的子協程,有沒有什麼辦法,通知該子協程退出呢?這種場景下,可以使用select+chan的機制。

123

4567

891011

1213

1415

1617

1819

2021

22

var stop chan bool

func reqtask(name string) }}

func main()

子協程使用 for 迴圈定時輪詢,如果stop通道有值,則退出,否則繼續輪詢。

123

4

worker1 send request

worker1 send request

worker1 send request

stop worker1

更複雜的場景如何做併發控制呢?比如子協程中開啟了新的子協程,或者需要同時控制多個子協程。這種場景下,select+chan的方式就顯得力不從心了。

go 語言提供了 context 標準庫可以解決這類場景的問題,context 的作用和它的名字很像,上下文,即子協程的下上文。context 有兩個主要的功能:

context.withcancel()建立可取消的 context 物件,即可以主動通知子協程退出。

使用 context 改寫上述的例子,效果與select+chan相同。

123

4567

891011

1213

1415

1617

1819

20

func reqtask(ctx context.context, name string) 	}}

func main()

123

4567

8910

func main()

為每個子協程傳遞相同的上下文ctx即可,呼叫cancel()函式後該 context 控制的所有子協程都會退出。

123

4567

8

worker1 send request

worker2 send request

worker1 send request

worker2 send request

worker1 send request

worker2 send request

stop worker1

stop worker2

如果需要往子協程中傳遞引數,可以使用context.withvalue()

123

4567

891011

1213

1415

1617

1819

2021

2223

2425

2627

type options struct

func 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 物件。

123

4567

8910

func main()

withtimeout()的使用與withcancel()類似,多了乙個引數,用於設定超時時間。執行結果如下:

123

4567

worker2 send request

worker1 send request

worker1 send request

worker2 send request

stop worker2

stop worker1

before cancel

因為超時時間設定為 2s,但是 main 函式中,3s 後才會呼叫cancel(),因此,在呼叫cancel()函式前,子協程因為超時已經退出了。

超時退出可以控制子協程的最長執行時間,那context.withdeadline()則可以控制子協程的最遲退出時間。

123

4567

891011

1213

1415

1617

1819

2021

2223

func reqtask(ctx context.context, name string) 	}}

func main()

執行結果如下:

123

45

worker2 send request

worker1 send request

stop worker2 context deadline exceeded

stop worker1 context deadline exceeded

before cancel

可以看到,子協程worker1worker2均是因為截止時間到了而退出。

Java併發程式設計教程

1 使用執行緒的經驗 設定名稱 響應中斷 使用threadlocal 2 executor executorservice和future 3 阻塞佇列 put和take offer和poll drainto 4 執行緒間的協調手段 lock condition wait notify notifya...

Java併發程式設計教程

多執行緒程式包含兩個或多個可同時執行的部分,每個部分可以同時處理不同的任務,從而能更好地利用可用資源,特別是當您的計算機有多個cpu時。多執行緒使您能夠寫入多個活動,可以在同一程式中同時進行操作處理。新執行緒 new 新執行緒在新的狀態下開始其生命週期。直到程式啟動執行緒為止,它保持在這種狀態。它也...

併發程式設計學習 併發程式設計的挑戰

死鎖 資源限制的挑戰 併發程式設計的目的是為了讓程式執行的更快,但是並不是啟動更多的執行緒,就能讓程式最大限度的併發執行。在進行併發程式設計時,如果希望通過多執行緒執行任務讓程式執行的更快,會面臨非常多的挑戰,比如上下文切換的問題,死鎖的問題,以及受限於硬體和軟體的資源限制問題 即使是單核處理器也支...