从goroutine捕获返回值


80

以下代码给出了编译错误,提示“意外运行”:

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}

我知道,如果正常调用函数就可以获取返回值,而无需使用goroutine。或者我可以使用频道等

我的问题是为什么不能从goroutine中获取像这样的返回值。


7
您可以使用渠道将其返回
rogerdpack

为什么允许goroutine具有返回值
srinath samala

1
@rogerdpack要求更改正在使用的任何函数的api。因此,如果不是您自己的,则可能需要包装函数
David Callanan

Answers:


65

严格的答案是您可以做到。这可能不是一个好主意。下面的代码可以做到这一点:

var x int
go func() {
    x = doSomething()
}()

这将产生一个新的goroutine,该goroutine将进行计算doSomething(),然后将结果分配给x。问题是:您将如何使用x原始goroutine?您可能要确保使用它完成了生成的goroutine,以便您没有竞争条件。但是,如果要执行此操作,则需要一种与goroutine进行通信的方法,并且如果可以执行此操作,为什么不仅仅使用它来将值发送回去呢?


6
您可以添加一个WaitGroup来确保您已完成并等待它。但是正如您所说,这不是唯一的方法,而是一个渠道。
Not_a_Golfer 2014年

1
这不是return,这是assign思想
Nidhin David '18

90

为什么无法从goroutine中获取将其分配给变量的返回值?

(异步地)运行goroutine和从函数获取返回值本质上是矛盾的动作。当您说的go是“异步执行”或什至更简单时:“继续!不要等待函数执行完成”。但是,当您将函数返回值分配给变量时,您期望变量中包含该值。因此,当您这样做时,x := go doSomething(arg)您会说:“继续,不要等待该函数!Wait-wait-wait!我需要x在下一行的var中访问一个返回值!”

频道

从goroutine中获取值的最自然的方法是通道。通道是连接并发goroutine的管道。您可以将值从一个goroutine发送到通道,然后将这些值接收到另一个goroutine或在同步函数中。您可以使用select以下方法从goroutine中轻松获取一个不破坏并发性的值:

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        // Await both of these values
        // simultaneously, printing each one as it arrives.
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        } 
    }
}

该示例取自Go By Example

CSP和消息传递

Go很大程度上基于CSP理论。上面的幼稚描述可以用CSP精确地概述(尽管我认为这超出了问题的范围)。我强烈建议您至少熟悉CSP理论,因为它是RAD。这些简短的引文给出了思考的方向:

顾名思义,CSP允许根据独立运行的组件过程来描述系统,并且仅通过消息传递通信相互交互。

在计算机科学中,消息传递将消息发送到流程,并依赖于流程和支持的基础结构来选择和调用要运行的实际代码。消息传递不同于常规编程,在常规编程中,通过名称直接调用过程,子例程或函数。


9

go关键字的想法是,您异步运行doSomething函数,并在不等待结果的情况下继续执行当前goroutine,就像在Bash shell中执行命令后跟“&”一样。如果你想做

x := doSomething(arg)
// Now do something with x

那么您需要当前的goroutine来阻止,直到doSomething完成。那为什么不只打电话给doSomething当前goroutine中?还有其他选择(例如,doSomething可以将结果发布到当前goroutine从中接收值的通道),但是简单地调用doSomething并将结果分配给变量显然很简单。


0

这是Go创作者的设计选择。有一大堆抽象/ API来代表的异步I / O操作的价值- ,promisefutureasync/awaitcallbackobservable等这些抽象/ API是天生绑调度的单位-协同程序-这些抽象/ API的规定如何协同程序(或更准确地说,可以构成它们代表的异步I / O的返回值

Go选择消息传递(也称为channel)作为抽象/ API,以表示异步I / O操作的返回值。当然,goroutine和通道为您提供了可组合的工具来实现异步I / O操作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.