在 Go 中处理 long-运行 操作时等待上下文 Done 通道取消
Wait for context Done channel for cancellation while working on long-run operation in Go
在这种情况下:
- 第一个 go 例程正在处理一个长运行 操作并阻塞直到它完成
- 第二个go例程可能随时取消整个任务
- 当整个任务被取消时,第一个go例程应该立即退出操作并return。
这是我的解决方案。它可以工作,但感觉不优雅或 Go 风格。
你能纠正我或告诉我更好的解决方案吗?
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var (
data int
readCh = make(chan struct{})
)
go func() {
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
readCh <- struct{}{}
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
select {
case <-ctx.Done():
log.Println("cancelled")
return
case <-readCh:
break
}
log.Println("got final data", data)
Close readCh
表示长 运行 goroutine 完成。与发送值相比,关闭通道有两个好处:
- 用 defer 方便地调用 close
- close 在上下文被取消的情况下不会阻塞。如果上下文在 goroutine 完成之前被取消,则问题中的代码会泄漏 goroutine。
这是更新后的代码:
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var (
data int
readCh = make(chan struct{})
)
go func() {
defer close(readCh)
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
select {
case <-ctx.Done():
log.Println("cancelled")
return
case <-readCh:
break
}
log.Println("got final data", data)
如果不需要区分long-运行 goroutine的完成和取消,从goroutine调用cancel函数。
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var data int
go func() {
defer cancel()
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
<-ctx.Done()
log.Println("got final data", data)
在这种情况下:
- 第一个 go 例程正在处理一个长运行 操作并阻塞直到它完成
- 第二个go例程可能随时取消整个任务
- 当整个任务被取消时,第一个go例程应该立即退出操作并return。
这是我的解决方案。它可以工作,但感觉不优雅或 Go 风格。
你能纠正我或告诉我更好的解决方案吗?
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var (
data int
readCh = make(chan struct{})
)
go func() {
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
readCh <- struct{}{}
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
select {
case <-ctx.Done():
log.Println("cancelled")
return
case <-readCh:
break
}
log.Println("got final data", data)
Close readCh
表示长 运行 goroutine 完成。与发送值相比,关闭通道有两个好处:
- 用 defer 方便地调用 close
- close 在上下文被取消的情况下不会阻塞。如果上下文在 goroutine 完成之前被取消,则问题中的代码会泄漏 goroutine。
这是更新后的代码:
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var (
data int
readCh = make(chan struct{})
)
go func() {
defer close(readCh)
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
select {
case <-ctx.Done():
log.Println("cancelled")
return
case <-readCh:
break
}
log.Println("got final data", data)
如果不需要区分long-运行 goroutine的完成和取消,从goroutine调用cancel函数。
var (
workTimeCost = 6 * time.Second
cancelTimeout = 5 * time.Second
)
ctx, cancel := context.WithCancel(context.Background())
var data int
go func() {
defer cancel()
log.Println("blocked to read data")
// fake long i/o operations
time.Sleep(workTimeCost)
data = 10
log.Println("done read data")
}()
// fake cancel is called from the other routine (it's actually not caused by timeout)
time.AfterFunc(cancelTimeout, cancel)
<-ctx.Done()
log.Println("got final data", data)