【问题标题】:Catching return values from goroutines从 goroutine 中捕获返回值
【发布时间】:2014-01-23 13:37:40
【问题描述】:

下面的代码给出了编译错误,说'unexpected go':

x := go doSomething(arg)

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

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

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

【问题讨论】:

  • 您可以使用频道返回它
  • 为什么它允许有一个 goroutine 的返回值
  • @rogerdpack 需要更改您正在使用的任何功能的 api。所以如果不是你自己的,你可能需要一个包装函数

标签: go concurrency goroutine


【解决方案1】:

为什么不能从分配给变量的 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 和消息传递

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

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

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

【讨论】:

  • 你的帽子确实是红色的!
【解决方案2】:

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

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

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

【讨论】:

  • 您可以添加一个 WaitGroup 以确保您已经完成并等待它。但正如你所说,这不是办法,渠道才是。
  • 这不是return,这是assignment
【解决方案3】:

go 关键字的想法是您异步运行 doSomething 函数,并继续当前的 goroutine 而无需等待结果,这有点像在 Bash shell 中执行带有“&”的命令。如果你想做

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

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

【讨论】:

    【解决方案4】:

    这是 Go 创作者的设计选择。有很多抽象/API 来表示异步 I/O 操作的价值 - promisefutureasync/awaitcallbackobservable 等。这些抽象/API 本质上与调度单元 - 协程 - 这些抽象/API 决定了协程(或更准确地说是由它们表示的异步 I/O 的返回值)如何组合

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

    【讨论】:

      猜你喜欢
      • 2016-09-09
      • 1970-01-01
      • 2018-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      相关资源
      最近更新 更多