从goroutines捕获返回值

Ner*_*rve 62 concurrency go goroutine

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

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}
Run Code Online (Sandbox Code Playgroud)

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

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

I15*_*159 68

为什么不能从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)
        } 
    }
}
Run Code Online (Sandbox Code Playgroud)

该示例取自Go By Example

CSP和消息传递

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

顾名思义,CSP允许在独立运行的组件流程方面描述系统,并且仅通过消息传递通信相互交互.

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


jos*_*hlf 50

严格的答案是你可以做到这一点.这可能不是一个好主意.这里的代码可以做到这一点:

var x int
go func() {
    x = doSomething()
}()
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以添加WaitGroup以确保已完成并等待它.但正如你所说,这不是通过这种方式实现它的方式. (5认同)
  • 这不是`return`,这是`assign`ment (4认同)

Mat*_*rog 8

go关键字的想法是你异步运行doSomething函数,并继续当前的goroutine而不等待结果,就像在Bash shell中执行一个带有'&'的命令一样.如果你想做

x := doSomething(arg)
// Now do something with x
Run Code Online (Sandbox Code Playgroud)

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


Bha*_*tri 6

这是 Go 创建者的设计选择。有大量的抽象/API 来表示异步 I/O 操作的值 - promisefutureasync/awaitcallback、等。这些抽象/API 本质上与调度单元 -协程observable相关- 并且这些抽象/API 决定了协程(或者更准确地说,它们所代表的异步 I/O 的返回值)可以由它们组成

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


arv*_*man 6

为什么不使用通道写入呢?

chanRes := make(chan int, 1)
go doSomething(arg, chanRes)
//blocks here or you can use some other sync mechanism (do something else) and wait
x := <- chanRes
func doSomething(arg int, out chan<- int){
    ...
    out <- my_int_value
}
Run Code Online (Sandbox Code Playgroud)