以下是我遇到的一個簡化例子。Go Playground here.
。主方法周期性地呼叫process函式。行程函式獲得了一個鎖,它必須在回傳之前或在應用程式因中斷而關閉之前釋放該鎖。
中斷是通過取消傳遞給process的背景關系來處理的。在我的例子中,我只是簡單地取消了背景關系。
我怎樣才能確保在取消背景關系時,解鎖邏輯被執行完畢呢?
ATM,在我的測驗中,該邏輯沒有被執行至完成。該例程被啟動,但在程式退出前似乎并未完成。
似乎主方法正在退出,并在半路上殺死了該例程。這有可能嗎?我如何才能確保該例程總是在程式退出前完成?謝謝!
package main
import (
"fmt"/span>
"context"/span>
"時間" "時間
"sync"
)
func main() {
ctx, cancel := context.WithCancel(context.background() )
var wg sync.WaitGroup
wg.Add(1)
go func{
fmt.Println("開始定期行程")
defer wg.Done()
ticker := time.NewTicker(20 * time.Millisecond)
for {
select {
case <-ticker.C:
process(ctx)
case <-ctx.Done():
ticker.Stop()
}
}
fmt.Println("完成定期行程")
}()
time.Sleep(100 * time.Millisecond)
//取消背景關系。
取消()
wg.Wait()
}
func process(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
<-ctx.Done()
//解鎖資源
fmt.Println("解鎖資源")
time.Sleep(20 * time.Millisecond)
fmt.Println("Unlocked resources")
}()
//做一些作業。
fmt.Println("開始作業")
//獲得鎖,在實際行程中。
//獲取鎖將失敗,除非 //獲取鎖將失敗。
//它已被前一行程釋放。
// run
fmt.Println("Acquired lock")
time.Sleep(10 * time.Millisecond)
fmt.Println("Finished process")
}
uj5u.com熱心網友回復:
按照目前的情況,你的程式將永遠不會退出。這是因為在main中啟動的goroutine中的for回圈從未退出(你在ctx.Done()時停止了ticker,但沒有退出回圈)。
第二個問題是,在process中,當函式退出時,goroutine被取消(通過defer cancel()),但由于延遲,goroutine將繼續運行一段時間。這里的解決方案取決于您是否需要異步發生 "解鎖"(我認為這并不重要)。
下面的方法(playground)解決了這兩個問題,并確保process在資源釋放之前不會回傳;如果您需要并行釋放這些資源,那么一個選擇是將WaitGroup傳遞給該函式)
package main
import (
"fmt"/span>
"context"/span>
"時間" "時間
"sync"
)
func main() {
ctx, cancel := context.WithCancel(context.background() )
var wg sync.WaitGroup
wg.Add(1)
go func{
fmt.Println("開始定期行程")
defer wg.Done()
ticker := time.NewTicker(20 * time.Millisecond)
for {
select {
case <-ticker.C:
process(ctx)
case <-ctx.Done():
ticker.Stop()
fmt.Println("Finished periodic process")
return。
}
}
}()
time.Sleep(100 * time.Millisecond)
取消()
wg.Wait()
}
func process(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
done := make(chan struct{}) //也可以使用等待組。
go func() {
<-ctx.Done()
fmt.Println("解鎖資源")
time.Sleep(10 * time.Millisecond)
fmt.Println("解鎖的資源")
close(done)
}()
//做一些作業。
fmt.Println("started process")
time.Sleep(10 * time.Millisecond)
fmt.Println("Finished process")
取消()
<-done //等待資源被解鎖。
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/313780.html
標籤:
