从goroutines捕获返回值

时间:2014-01-06 07:28:10

标签: go concurrency goroutine

我是golang的新手,所以请原谅这是一个非常基本的问题。下面的代码给出了编译错误,说'意外去':

x := go doSomething(arg)

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

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

我的问题是为什么无法从goroutine中获取这样的返回值。

4 个答案:

答案 0 :(得分:55)

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

运行goroutine(异步)并从函数中获取返回值基本上是矛盾的动作。当你说go时,你的意思是“异步”或甚至更简单:“继续!不要等待功能执行完成”。但是,当您将函数返回值赋给变量时,您希望在变量中包含此值。所以,当你这样做x := go doSomething(arg)时,你会说:“继续,不要等待这个功能!等待等待!我需要一个返回值,可以在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&amp;消息传递

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

  

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

     

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

答案 1 :(得分:48)

严格的答案是你可以这样做。这可能不是一个好主意。这是代码:

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

这将产生一个新的goroutine,它将计算doSomething(),然后将结果分配给x。问题是:你如何使用原始goroutine中的x?您可能希望确保生成的goroutine已完成,以便您没有竞争条件。但是如果你想这样做,你需要一种与goroutine通信的方法,如果你有办法做到这一点,为什么不用它来发回价值?

答案 2 :(得分:8)

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

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

然后你需要阻止当前的goroutine,直到doSomething结束。那么为什么不在当前的goroutine中调用中的doSomething ?还有其他选项(例如,doSomething可以将结果发布到通道,当前goroutine从中接收值),但只是调用doSomething并将结果分配给变量显然更简单。

答案 3 :(得分:0)

这是Go创作者的设计选择。有很多抽象/ API表示异步I / O操作的值-promisefutureasync/awaitcallbackobservable等这些抽象/ API固有地与调度单位-协程-相关,并且这些抽象/ API规定了协程(或更确切地说,由它们代表的异步I / O的返回值)如何< em>组成

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