Go使用context控制協(xié)程取消的實(shí)戰(zhàn)案例
在并發(fā)編程中,合理地控制協(xié)程(goroutine)的生命周期是保證程序穩(wěn)定性和資源可控使用的關(guān)鍵。Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的 context 包正是為了解決這一問(wèn)題而生。它為我們提供了取消信號(hào)、超時(shí)控制、請(qǐng)求作用域的值傳遞等功能。
本文將通過(guò)一個(gè)實(shí)際案例,演示如何使用 context 控制協(xié)程的取消,避免資源泄露,實(shí)現(xiàn)優(yōu)雅退出。
一、什么是 context
context 是 Go 1.7 起加入標(biāo)準(zhǔn)庫(kù)的一個(gè)重要包,用于跨 API 邊界傳遞取消信號(hào)、超時(shí)時(shí)間、截止時(shí)間等信息。
主要接口定義如下:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
其中最關(guān)鍵的是:
Done():返回一個(gè) channel,當(dāng) context 被取消或超時(shí)關(guān)閉時(shí),該 channel 會(huì)被關(guān)閉;Err():返回取消的原因,例如context.Canceled或context.DeadlineExceeded。
二、常見(jiàn)創(chuàng)建方式
Go 提供了以下常用方式創(chuàng)建 context:
ctx := context.Background() // 最頂層、永不取消的 context ctx, cancel := context.WithCancel(parent) // 手動(dòng)調(diào)用 cancel() 取消 ctx, cancel := context.WithTimeout(parent, 3*time.Second) // 指定超時(shí)時(shí)間 ctx, cancel := context.WithDeadline(parent, time.Now().Add(3*time.Second)) // 到期時(shí)間點(diǎn)
這些 context 都可以傳遞到協(xié)程中,通過(guò) ctx.Done() 控制協(xié)程的停止。
三、實(shí)戰(zhàn)案例:使用 context 控制任務(wù)協(xié)程
場(chǎng)景描述
假設(shè)我們要運(yùn)行一個(gè)任務(wù),該任務(wù)每秒輸出一次“正在處理”,但當(dāng)主程序在某個(gè)時(shí)機(jī)需要終止它(比如點(diǎn)擊“停止按鈕”或超時(shí)),我們要優(yōu)雅地通知協(xié)程退出。
示例代碼
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("worker 任務(wù)被取消:", ctx.Err())
return
default:
fmt.Println("worker 正在處理任務(wù)...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
// 主線程運(yùn)行5秒后取消任務(wù)
time.Sleep(5 * time.Second)
cancel()
// 等待協(xié)程打印結(jié)束語(yǔ)
time.Sleep(1 * time.Second)
fmt.Println("主程序退出")
}
輸出結(jié)果
worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 任務(wù)被取消: context canceled 主程序退出
可以看到,主程序通過(guò) cancel() 取消了 context,協(xié)程立即響應(yīng)退出了。
四、使用 context.WithTimeout 實(shí)現(xiàn)超時(shí)控制
除了手動(dòng)調(diào)用 cancel,我們還可以通過(guò)設(shè)定超時(shí)時(shí)間自動(dòng)取消任務(wù)。
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(5 * time.Second) // 主線程等待更久,看任務(wù)是否能自動(dòng)終止
fmt.Println("主程序退出")
}
運(yùn)行結(jié)果類似:
worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 正在處理任務(wù)... worker 任務(wù)被取消: context deadline exceeded 主程序退出
這表示:即使主程序沒(méi)有主動(dòng)調(diào)用 cancel(),協(xié)程也在 3 秒后收到 context 超時(shí)通知并正常退出。
五、多個(gè)協(xié)程共享一個(gè) context
我們可以啟動(dòng)多個(gè)協(xié)程,并使用同一個(gè) context 控制它們:
func main() {
ctx, cancel := context.WithCancel(context.Background())
for i := 1; i <= 3; i++ {
go func(id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("協(xié)程 %d 接收到取消信號(hào)\n", id)
return
default:
fmt.Printf("協(xié)程 %d 正在工作\n", id)
time.Sleep(1 * time.Second)
}
}
}(i)
}
time.Sleep(4 * time.Second)
cancel()
time.Sleep(1 * time.Second)
fmt.Println("主程序退出")
}
輸出:
協(xié)程 1 正在工作 協(xié)程 2 正在工作 協(xié)程 3 正在工作 ...(多次輸出) 協(xié)程 1 接收到取消信號(hào) 協(xié)程 2 接收到取消信號(hào) 協(xié)程 3 接收到取消信號(hào) 主程序退出
這樣我們實(shí)現(xiàn)了“一鍵終止所有協(xié)程”。
六、最佳實(shí)踐建議
- 永遠(yuǎn)使用
context.WithCancel/WithTimeout返回的cancel()函數(shù),不要忘記defer cancel(); - 在需要可控中止的任務(wù)(如網(wǎng)絡(luò)請(qǐng)求、數(shù)據(jù)庫(kù)操作、循環(huán)處理)中傳入 context;
- 對(duì)
ctx.Done()的監(jiān)聽(tīng)要放在協(xié)程內(nèi)部適當(dāng)位置,防止資源泄露; - 在請(qǐng)求鏈中傳遞 context,以實(shí)現(xiàn)鏈路級(jí)別的取消與超時(shí)控制。
七、結(jié)語(yǔ)
context 是 Go 并發(fā)控制中不可或缺的利器。它不僅解決了協(xié)程取消的痛點(diǎn),還能為任務(wù)設(shè)置統(tǒng)一的生命周期控制邏輯,是構(gòu)建高可靠網(wǎng)絡(luò)服務(wù)、后臺(tái)任務(wù)系統(tǒng)、爬蟲(chóng)等并發(fā)程序的基礎(chǔ)設(shè)施。
如果你還沒(méi)有在項(xiàng)目中大量使用它,不妨從今天開(kāi)始重構(gòu)代碼,使用 context 管理協(xié)程生命周期,讓你的 Go 程序更穩(wěn)定、更可控!
以上就是Go使用context控制協(xié)程取消的實(shí)戰(zhàn)案例的詳細(xì)內(nèi)容,更多關(guān)于Go context控制協(xié)程取消的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
為什么Go里值為nil可以調(diào)用函數(shù)原理分析
這篇文章主要為大家介紹了為什么Go里值為nil可以調(diào)用函數(shù)原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
golang標(biāo)準(zhǔn)庫(kù)crc32的使用樣例
根據(jù)實(shí)驗(yàn)我們知道crc32算法比md5算法快4倍左右,所以研究了下golang的crc32使用,這篇文章主要給大家介紹了關(guān)于golang標(biāo)準(zhǔn)庫(kù)crc32使用的相關(guān)資料,需要的朋友可以參考下2024-03-03
Golang創(chuàng)建第一個(gè)web項(xiàng)目(Gin+Gorm)
本文主要介紹了Golang創(chuàng)建第一個(gè)web項(xiàng)目(Gin+Gorm),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06
一文帶你了解Go語(yǔ)言中鎖特性和實(shí)現(xiàn)
Go語(yǔ)言中的sync包主要提供的對(duì)并發(fā)操作的支持,標(biāo)志性的工具有cond(條件變量)?once?(原子性)?還有?鎖,本文會(huì)主要向大家介紹Go語(yǔ)言中鎖的特性和實(shí)現(xiàn),感興趣的可以了解下2024-03-03
go 異常處理panic和recover的簡(jiǎn)單實(shí)踐
在Go語(yǔ)言中,異常處理主要通過(guò)panic和recover這兩個(gè)內(nèi)建函數(shù)來(lái)實(shí)現(xiàn),本文主要介紹了go異常處理panic和recover的簡(jiǎn)單實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04
Go語(yǔ)言Elasticsearch數(shù)據(jù)清理工具思路詳解
這篇文章主要介紹了Go語(yǔ)言Elasticsearch數(shù)據(jù)清理工具思路詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Golang實(shí)現(xiàn)AES加密和解密的示例代碼
AES( advanced encryption standard)使用相同密鑰進(jìn)行加密和解密,也就是對(duì)稱加密。本文將詳細(xì)講解Golang實(shí)現(xiàn)AES加密和解密的方法,感興趣的可以學(xué)習(xí)一下2022-05-05
go 類型轉(zhuǎn)換方式(interface 類型的轉(zhuǎn)換)
這篇文章主要介紹了go 類型轉(zhuǎn)換方式(interface 類型的轉(zhuǎn)換),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05

