在Go语言中,是否可能从多个goroutine中接收结果?

huangapple go评论109阅读模式
英文:

Is it possible to receive a result from one of a number of goroutines in Go?

问题

我最近才了解到谷歌的编程语言Go。我对它提供的并发支持很感兴趣,并开始了解更多相关信息。然而,我想了解Go如何实现并发的特定功能,但到目前为止,我还没有看到任何证据表明这个功能存在。

这里有一个假设的情况:假设我们正在编写一个函数来确定特定输入的Foo值。对于任何给定的输入,Foo值要么在域A中找到,要么在域B中找到(不会同时存在于两个域中)。在这些域中搜索的技术是非常不同的,但它们共享一个特性,即成功的搜索往往会很快返回,而不成功的搜索必须遍历整个数据集以穷尽所有可能,因此需要很长时间。

在其他使用并发的语言(如Cilk)中,可以编写一个函数Foosearch,它会同时启动一个Asearch函数和一个Bsearch函数。这些函数将并发运行,只要其中任何一个找到答案,就会将答案报告给调用函数Foosearch,然后Foosearch会终止尚未返回的函数。

然而,使用Go的goroutines,看起来只能通过通道连接两个例程 - 因此,无法设置一个通道,Asearch或Bsearch可以根据哪个先找到答案来发送,然后让Foosearch从中读取。同时,看起来无法在没有阻塞的情况下从通道中读取 - 因此,无法让Foosearch启动Asearch和Bsearch,并设置两个通道,然后在循环中运行以检查是否有一个或另一个已经产生了答案。

我的对Go并发限制的理解正确吗?是否有其他方法可以实现给定的结果?

英文:

I've only just recently learned about Google's programming language, Go. I've been intrigued by its offered support for concurrency, and set out to learn more about it. However, I went looking to see how Go implemented a particular feature of concurrency, and so far I haven't seen any evidence at all that this feature is there at all.

Here's a hypothetical situation: suppose we are programming a function to determine the Foo value of a particular input. For any given input, the Foo value is found either in domain A, or domain B (not in both). The techniques of searching in these domains are quite different, but they share the property that successful searches tend to return quickly, while unsuccessful searches must go through the entire dataset to be exhaustive and therefore take a long time.

Now, in other languages that employ concurrency (such as Cilk) one could program the function Foosearch so that it spawned an Asearch function and a Bsearch function. These functions would run concurrently, and whenever either of them came up with an answer, that answer would be reported to the calling function Foosearch, which would terminate any functions it had spawned that had not returned.

With Go's goroutines, however, it looks like you can only connect two routines with a channel - so you couldn't set up a channel to which either Asearch or Bsearch could send, depending on which found an answer first, and have Foosearch read from it. It also looks like you can't read from a channel without blocking on it - so you couldn't have Foosearch start Asearch and Bsearch and set up channels from both, then run in a loop checking to see if one or the other has produced an answer.

Is my understanding of the limits of Go's concurrency correct? Is there another way to achieve the given result?

答案1

得分: 16

不,我不相信你对Go的限制的理解是正确的。

首先,我在Go中没有看到任何将通道限制为在两个例程之间通信的东西。你可以将同一个通道传递给Asearch和Bsearch,然后无论哪个完成都可以在该通道上发送结果。

如果你想使用两个通道,并等待其中一个获得结果,你可以使用select语句。从Go教程中,一个在用于发送请求的通道和一个用于向服务器发出退出信号的通道上进行选择的示例:

21    func server(op binOp, service chan *request, quit chan bool) {
22        for {
23            select {
24            case req := <-service:
25                go run(op, req);  // 不等待它
26            case <-quit:
27                return;
28            }
29        }
30    }

此外,虽然从通道接收通常会阻塞,但你也可以从通道进行非阻塞接收

> 如果接收表达式用于赋值或初始化形式
>
> x, ok = <-ch
> x, ok := <-ch
> var x, ok = <-ch
>
> 接收操作将变为非阻塞。如果操作可以继续进行,布尔变量ok将设置为true,并将值存储在x中;否则,ok设置为false,x设置为其类型的零值(参见零值)。

因此,有多种方法可以等待多个goroutine的结果而不阻塞。我认为我会使用使用select多路复用的多个通道,因为这样你可以轻松地知道哪个例程返回了结果,而不必将该信息打包到你发送的值中或进行其他形式的带外通信。

英文:

No, I don't believe your understanding of the limits of Go is correct.

For one thing, there's nothing I've seen in Go that limits channels to communicating between two routines. You can pass the same channel to both Asearch and Bsearch, and then whichever one finishes can send the result on that channel.

If instead you wanted to use two channels, and wait for one of them to get the result, you could just use the select statement. From the Go tutorial, an example selecting on a channel used to send requests, and one used to signal the server to quit:

21    func server(op binOp, service chan *request, quit chan bool) {
22        for {
23            select {
24            case req := &lt;-service:
25                go run(op, req);  // don&#39;t wait for it
26            case &lt;-quit:
27                return;
28            }
29        }
30    }

Furthermore, while receives from a channel normally block, you can also do a non-blocking receive from a channel.

> If a receive expression is used in an
> assignment or initialization of the
> form
>
> x, ok = <-ch
> x, ok := <-ch
> var x, ok = <-ch
>
> the receive operation becomes
> non-blocking. If the operation can
> proceed, the boolean variable ok will
> be set to true and the value stored in
> x; otherwise ok is set to false and x
> is set to the zero value for its type
> (§The zero value).

So, there are several ways to wait for the result from multiple goroutines without blocking. I think I'd go with multiple channels multiplexed using select, as that way you can easily tell which routine returned the result without having to package that information into the value you're sending or doing some other form of out-of-band communication.

答案2

得分: 5

你可以使用select关键字从多个通道接收值。

值将从最先有结果的通道中取出。

var c1, c2 chan int;
var result int;

select {
case result = <-c1:
    print("从c1接收到", result, "\n");
case result = <-c2:
    print("从c2接收到", result, "\n");
}

参考资料

英文:

You can use the select keyword to receive from multiple channels.

The value will be taken from the channel that has the result earlier than the others.

var c1, c2 chan int;
var result int;

select {
case result = &lt;-c1:
	print(&quot;received &quot;, result, &quot; from c1\n&quot;);
case result = &lt;-c2:
	print(&quot;received &quot;, result, &quot; from c2\n&quot;);
}

Reference

huangapple
  • 本文由 发表于 2009年11月16日 01:46:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/1738236.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定