golang惯用的方式来阻止

时间:2015-10-28 20:00:47

标签: go goroutine

我是Go的新手,所以如果我的问题的答案显而易见,我会提前道歉:)

我计划一个生产者来读取文件并将每一行发送到一个频道,例如:

scanner := bufio.NewScanner(file)
for scanner.Scan() {
    processingChan <- scanner.Text()
}

并添加一些goroutine来消耗这些行。

现在,我想要的是,如果任何行无法在goroutine中处理(让我们说这行包含我的业务规则的无效值),我想停止生产者循环,关闭文件(已经推迟了并完成了该计划。

问题是:我怎样才能通知&#34;生产者循环/停止?

我发现有人建议:

for scanner.Scan() {
    select {
    case <- quit:
        // break / return
    default:
        // send next line to channel
    }
}

并且消费者goroutines将写入&#34; quit&#34;发生故障时(或错误)通道。

这种方法可能解决了这个问题,但我想知道是否有更清洁/更好或只是普通/流行的方法。

3 个答案:

答案 0 :(得分:3)

更正,使用退出频道。特别是当你已经在循环中发送到频道时,处理额外的频道很容易。但是,我不会使用您提出的表单,而是更简单且更安全版本:

for scanner.Scan() {
    select {
    case <- quit:
        return
    case processingChan <- scanner.Text():
    }
}

为什么它更安全?因为它没有死锁,与default的示例相反。你可能很幸运,从来没有遇到它,但有些情况你会。问题在于你有两个相互交流的例程,总是需要更多关注。考虑一下:

quit := make(chan error, 1)
prod := make(chan int)

go func() {
    for n := range prod {
        runtime.Gosched()
        if n%66 == 0 {
            quit <- errors.New("2/3 of evil")
            return
        }
    }
}()

for n := 1; n < 1000; n++ {
    select {
    case <-quit:
        fmt.Println(n)
        return
    default:
        prod <- n
    }
}

// https://play.golang.org/p/3kDRAAwaKR

轰!主要例程是尝试发送到prod频道,但没有人接收它;与我们的消费者一样的问题。

向频道添加缓冲区也不能解决问题,但会减少它的可能性。

将前面的示例与以下更改进行比较:

select {
case <-quit:
    fmt.Println(n)
    return
case prod <- n:
}

// https://play.golang.org/p/pz8DMYdrVV

很好地工作。

我理解一个人想要使用第一个选项以确保它们尽早退出,但如果您在退出之前发送一个或两个额外的项目进行处理,这通常不是一个大问题。

答案 1 :(得分:0)

我想你回答了自己的问题。在我看来,这是最干净最惯用的方法。而且我认为大多数Gophers会同意。其他选项是通过一些你必须包装在Mutex中的变量来共享状态,你仍然有一个类似的for循环虽然它会在顶部或底部有一个if来检查标志看它是否应该中止。

我认为在创建使用者和生成器结构时,最好的设计是清楚地命名要在ReadFilesAsync之类的Go例程中运行的方法,并在同一结构上定义退出和/或中止通道。它为您的课程的消费者提供了一个干净,简单,一致的交互方式。如果方法是异步的,则在go例程中调用它。如果要停止它,您调用方法的对象也会公开并中止通道,您可以发出信号来执行此操作。这消除了对锅炉板代码的需要,例如在调用范围中声明中止通道并将其传递给异步方法。

编辑:请注意,这更多是关于停止goroutine而不是停止for。这只是放入breakreturn的问题。如果for循环在goroutine中运行,则仅存在协调需求。

答案 2 :(得分:0)

我不觉得自己很专业,但我觉得你的方法很好。我可以想象与goroutine有点不同的方法。 goroutines的输入将是符文的通道。比要求停止可能是关闭输入通道,结果可能是每次迭代的结果。

但是在这个简单的情况下它可能比你的代码更长更慢,因此我写了一个很好的代码。