下面的代码给出了编译错误,提示“意外运行”:
x := go doSomething(arg) func doSomething(arg int) int{ ... return my_int_value }
我知道,如果我正常调用函数,即不使用 goroutine 或者我可以使用通道等,我可以获取返回值。
我的问题是为什么不可能从 goroutine 中获取这样的返回值。
严格的回答是你可以做到。这可能不是一个好主意。这是可以做到这一点的代码:
var x int go func() { x = doSomething() }()
这将产生一个新的 goroutine,它将计算doSomething()并将结果分配给x. 问题是:你将如何使用x原始 goroutine?你可能想确保生成的 goroutine 已经完成,这样你就不会出现竞争条件。但是如果你想这样做,你需要一种与 goroutine 通信的方法,如果你有办法做到这一点,为什么不直接使用它来发送值呢?
doSomething()
x
运行 goroutine(异步)和从函数中获取返回值本质上是相互矛盾的行为。当你说go你的意思是“异步执行”或者更简单:“继续!不要等待函数执行完成”。但是,当您将函数返回值分配给变量时,您希望在变量中包含该值。因此,当您这样做时,x := go doSomething(arg)您是在说:“继续,不要等待函数!等待-等待-等待!我需要x在下一行的 var 中可以访问返回值!”
go
x := go doSomething(arg)
从 goroutine 中获取值的最自然的方法是通道。Channels是连接并发 goroutine 的Channels。您可以将值从一个 goroutine 发送到Channels,然后将这些值接收到另一个 goroutine 或同步函数中。您可以使用以下方法轻松地从不破坏并发性的 goroutine 中获取值select:
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
Go 主要基于CSP 理论。上面的幼稚描述可以根据 CSP 精确概述(尽管我认为这超出了问题的范围)。我强烈建议您熟悉 CSP 理论,至少因为它是 RAD。这些简短的引文给出了一个思考方向:
顾名思义,CSP 允许根据独立运行的组件进程来描述系统,并且仅通过消息传递通信相互交互。 在计算机科学中,消息传递向进程发送消息,并依赖进程和支持基础设施来选择和调用要运行的实际代码。消息传递与传统编程不同,在传统编程中,进程、子例程或函数是通过名称直接调用的。
顾名思义,CSP 允许根据独立运行的组件进程来描述系统,并且仅通过消息传递通信相互交互。
在计算机科学中,消息传递向进程发送消息,并依赖进程和支持基础设施来选择和调用要运行的实际代码。消息传递与传统编程不同,在传统编程中,进程、子例程或函数是通过名称直接调用的。