英文:
How to know if the context has been cancelled?
问题
如何判断上下文是否被取消?
在下面的示例代码中:
- 有两个任务。
- 如果其中任何一个任务先完成,我想通过上下文取消在其他任务中得知。
- 作为示例,task2将始终先完成(在task1之前)。
package main
import (
"context"
"fmt"
"time"
)
func task2(ctx context.Context, ch chan int) {
for i := 0; i <= 10; i++ {
if ctx.Err() != nil {
// 如果task1先完成,
// 我想打印这个错误。我该如何到达这个代码块?
fmt.Println("Cancelled 2", ctx.Err())
}
fmt.Println("Task2 ===== ", i)
time.Sleep(1 * time.Second)
if i == 2 {
ch <- 2
}
}
}
func task1(ctx context.Context, ch chan int) {
for i := 0; i <= 10; i++ {
if ctx.Err() != nil {
// 如果task2先完成,
// 我想打印这个错误。我该如何到达这个代码块?
fmt.Println("Cancelled 1", ctx.Err())
}
fmt.Println("Task1 ----- ", i)
time.Sleep(1 * time.Second)
if i == 5 {
ch <- 1
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan int)
go task1(ctx, ch)
go task2(ctx, ch)
d := <-ch
cancel() // 如果task1或task2中的任何一个完成,调用cancel()函数
fmt.Println("首先完成的任务:", d)
}
英文:
How to know if a context has been cancelled?
In a following sample code,
- There are 2 tasks.
- If any of these tasks completes first, I want to know in other tasks through context cancel.
- As part of example, task2 will always finish first (before task1).
package main
import (
"context"
"fmt"
"time"
)
func task2(ctx context.Context, ch chan int) {
for i := 0; i <= 10; i++ {
if ctx.Err() != nil {
// In case task1 completes first,
// I want to print this error. How do I reach this block?
fmt.Println("Cancelled 2", ctx.Err())
}
fmt.Println("Task2 ===== ", i)
time.Sleep(1 * time.Second)
if i == 2 {
ch <- 2
}
}
}
func task1(ctx context.Context, ch chan int) {
for i := 0; i <= 10; i++ {
if ctx.Err() != nil {
// In case task2 completes first,
// I want to print this error. How do I reach this block?
fmt.Println("Cancelled 1", ctx.Err())
}
fmt.Println("Task1 ----- ", i)
time.Sleep(1 * time.Second)
if i == 5 {
ch <- 1
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan int)
go task1(ctx, ch)
go task2(ctx, ch)
d := <-ch
cancel() // If either of task1 or task2 completes, invoke the cancel() func
fmt.Println("Task which completed first: ", d)
}
答案1
得分: 4
由于您的通道是无缓冲的,在main()
函数中只有一个对ch
的读取操作,所以task1
将在ch <- 1
处发生死锁。为了解决这个问题,您可以将其转换为一个选择语句,检查ctx.Done()
:
if i == 5 {
select {
case ch <- 1:
return
case <-ctx.Done():
fmt.Println("Cancelled 1", ctx.Err())
return
}
}
您可以在task2
中执行相同的操作。
请注意,当main()
函数终止时,任何仍然存在的Go协程也将随之终止,无论它们当时正在执行什么操作。如果您不希望如此,您需要提供额外的同步,例如使用sync.WaitGroup
,如下所示:
func a(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Second)
fmt.Println("a is done!")
}
func b(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Second*2)
fmt.Println("b is done, too!")
}
func main() {
wg := &sync.WaitGroup{}
wg.Add(2)
go a(wg)
go b(wg)
wg.Wait()
fmt.Println("Everyone is done! We can terminate without interrupting anyone.")
}
英文:
Since your channel is unbuffered, task1
will deadlock in ch <- 1
since you only have one read on ch
in main()
. To fix this, you can turn this into a select-statement where you check for ctx.Done()
:
if i == 5 {
select {
case ch <- 1:
return
case <-ctx.Done():
fmt.Println("Cancelled 1", ctx.Err())
return
}
}
You can do the same in task2
.
Do notice that when main()
terminates, any go routines still around will terminate along with it, no matter what they were in the process of doing. If you do not want that, you need to provide additional synchronization, for example using a sync.WaitGroup
like so:
func a(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Second)
fmt.Println("a is done!")
}
func b(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Second*2)
fmt.Println("b is done, too!")
}
func main() {
wg := &sync.WaitGroup{}
wg.Add(2)
go a(wg)
go b(wg)
wg.Wait()
fmt.Println("Everyone is done! We can terminate without interrupting anyone.")
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论