当前位置: 首页 > 知识库问答 >
问题:

go - Golang 中 context 执行了Cancel,但是<- ctx.Done() 没有执行?

羊舌子瑜
2024-09-23
package main

import (
    "context"
    "fmt"
)

func gen(ctx context.Context) <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        n := 0
        for {
            select {
            case <-ctx.Done():
                fmt.Println("done")
            default:
                n += 1
                ch <- n
            }
        }

    }()
    return ch
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
    defer cancel()
}

期望 能打印出 "done"

共有2个答案

唐焕
2024-09-23

应该是阻塞在 ch<-n 了。

cancel() 如果执行的比较晚,在 cancel 之前第六个 ch<-n 就已经执行了的话,就阻塞了。(main 逻辑相对比较长,比如还有一个 Println ,所以大概率 cancel 会比较晚。

可以试试这个:

package main

import (
    "context"
    "fmt"
)

func gen(ctx context.Context) <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        n := 0
        for {
            select {
            case <-ctx.Done():
                fmt.Println("done")
                close(ch) // 关闭 channel ,range 结束
                return
            default:
                n += 1
                ch <- n
            }
        }

    }()
    return ch
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            cancel()
            // break 不能 break,否则没有从 ch 中的读取,就会一直阻塞在 ch <- n
        }
    }
}

这个依然会打印 6 个,因为 cancel 的时候第六个 ch<-n 已经执行了。

狄冥夜
2024-09-23

在你的代码中,<-ctx.Done() 没有执行并打印 "done" 的原因是 cancel() 函数调用发生在 for n := range gen(ctx) 循环结束后,而循环结束的条件是 n == 5。由于 cancel() 是在循环结束后被调用,此时 gen 函数中的 goroutine 已经因为 main 函数中的循环结束而不再从 ch 发送数据,因此 select 语句中的 case <-ctx.Done(): 分支没有机会被执行。

为了解决这个问题,你需要确保在 main 函数中,在循环结束之前调用 cancel() 函数,并且让 gen 函数中的 goroutine 有机会响应 ctx.Done() 信号。你可以通过修改 main 函数来实现这一点,如下所示:

package main

import (
    "context"
    "fmt"
    "time"
)

func gen(ctx context.Context) <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        n := 0
        for {
            select {
            case <-ctx.Done():
                fmt.Println("done")
                return // 确保goroutine在接收到取消信号后退出
            default:
                n += 1
                ch <- n
                // 为了避免goroutine过快地发送数据,可以添加一点延迟
                time.Sleep(100 * time.Millisecond)
            }
        }
    }()
    return ch
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    done := make(chan bool)
    go func() {
        for n := range gen(ctx) {
            fmt.Println(n)
            if n == 5 {
                cancel() // 在n==5时取消context
                done <- true
                break
            }
        }
    }()

    <-done // 等待goroutine完成
}

在这个修改后的版本中,我添加了一个 done 通道来确保 main 函数等待 gen 函数中的 goroutine 完成其工作。当 n 等于 5 时,cancel() 被调用,并且 done 通道被发送一个值以通知 main 函数可以安全地继续执行。同时,我在 gen 函数的 goroutine 中添加了一个 return 语句,以确保在接收到取消信号后 goroutine 能够退出。此外,我还添加了一个小的延迟来模拟数据生成的过程,以避免在测试时过快地达到 n == 5 的条件。

 类似资料:
  • 最近,我使用了ThreadPoolExecutor和priorityqueue,并在将来遇到了这两种方法。对未来任务执行cancel()。和任务。remove(),将其从队列中移除。 更好的选择是什么?有什么区别吗?我可以保存两者的列表(从submit()接收的未来对象或任务本身),不确定要使用什么。。。 移除: 取消: 我使用了以下方法:http://docs.oracle.com/javase

  • 问题内容: 我有一个包含单元测试的Go文件,其中一些使用了公共变量。我正在测试的代码中使用了另一个全局变量。所有这些都可能导致问题。 在Go中,当我们执行位于同一文件中的测试时,它们如何运行?并行还是下一个不会在前一个完成之前开始? 问题答案: 测试起来真的很容易: 使用运行它,输出显示它是顺序的: 因此,正常的测试是依次执行的,但是请不要忘记未定义顺序:如何依次运行golang测试? 还要注意,

  • 问题内容: 我已经尝试了这两个代码,但是却没有执行,有人可以告诉我为什么吗? 提前致谢 问题答案: 试试这个代码: 它会在源元素的位置单击并按住,移至目标元素的位置,然后释放鼠标。 要么 它将单击并按住源元素的位置,移动给定的偏移量,然后释放鼠标。 要么 它将执行以上两个代码的操作。 我在Java上编写此代码。您可以转换为指定的语言。 从动作引用。

  • 在上面的例子中,我们演示了一下如何去触发执行一个外部的进程。我们这样做的原因是我们希望从Go进程里面可以访问外部进程的信息。但有的时候,我们仅仅希望执行一个外部进程来替代当前的Go进程。这个时候,我们需要使用Go提供的exec函数。 package main import "syscall" import "os" import "os/exec" func main() { // 本例中

  • 嘿,我在POM中做了这个配置。xml文件并行运行测试。但当我使用cmd进行“mvn验证”时,只有一个浏览器正在运行一个功能,而在完成一个功能文件的执行后,另一个功能正在运行。这是我的代码和pom。xml请建议我怎么做? 我正在使用cucumber 这是我的pom.xml代码: 提前感谢。

  • 我制作了以下cronjob sh文件: 手动运行此文件时,作业将正确运行。