一尘不染

从 goroutine 中获取返回值

go

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

x := go doSomething(arg)

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

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

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


阅读 377

收藏
2021-11-30

共2个答案

一尘不染

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

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

这将产生一个新的 goroutine,它将计算doSomething()并将结果分配给x. 问题是:你将如何使用x原始 goroutine?你可能想确保生成的 goroutine 已经完成,这样你就不会出现竞争条件。但是如果你想这样做,你需要一种与 goroutine 通信的方法,如果你有办法做到这一点,为什么不直接使用它来发送值呢?

2021-11-30
一尘不染

为什么不能从 goroutine 中获取返回值并将其分配给变量?

运行 goroutine(异步)和从函数中获取返回值本质上是相互矛盾的行为。当你说go你的意思是“异步执行”或者更简单:“继续!不要等待函数执行完成”。但是,当您将函数返回值分配给变量时,您希望在变量中包含该值。因此,当您这样做时,x := go doSomething(arg)您是在说:“继续,不要等待函数!等待-等待-等待!我需要x在下一行的 var 中可以访问返回值!”

Channels

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

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 允许根据独立运行的组件进程来描述系统,并且仅通过消息传递通信相互交互。

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

2021-11-30