捕获 goroutine 的返回值

问题描述 投票:0回答:6

下面的代码给出编译错误,提示“unexpected go”:

x := go doSomething(arg)

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

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

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

go concurrency goroutine
6个回答
182
投票

为什么无法从 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 理论。上面的简单描述可以用 CSP 来精确概述(尽管我认为这超出了问题的范围)。我强烈建议您至少熟悉 CSP 理论,因为它是 RAD。这些简短的引文给出了思考的方向:

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

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


95
投票

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

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

这将产生一个新的 goroutine,它将计算

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


9
投票

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

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

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


6
投票
promise

future
async/await
callback
observable
等。这些抽象/API 本质上与调度单元 -
coroutines
- 这些抽象/API 决定了如何协程(或者更准确地说,由它们表示的异步 I/O 的返回值)可以是composed Go 选择消息传递(又名

channels

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


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 }



0
投票

    渠道沟通 - 需要返回什么,通过渠道传递即可。
  1. 传递指针 - 将指向结果变量的指针传递给协程。
  2. 在父函数中定义并调用你的 goroutine 函数:
  3. func MyFunction(arg string) []string { result := make([]string, 0) var wg sync.WaitGroup wg.Add(1) go func(input string) { defer wg.Done() // Add the output to result variable }(input) wg.Wait() return result }
	
© www.soinside.com 2019 - 2024. All rights reserved.