Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang goroutine cannot use function return value(s)

I wish I could do something like the following code with golang:

package main

import (
    "fmt"
    "time"
)

func getA() (int) {

    fmt.Println("getA: Calculating...")
    time.Sleep(300 * time.Millisecond)

    fmt.Println("getA: Done!")
    return 100
}

func getB() (int) {

    fmt.Println("getB: Calculating...")
    time.Sleep(400 * time.Millisecond)

    fmt.Println("getB: Done!")
    return 200
}

func main() {

    A := go getA()
    B := go getB()

    C := A + B // waits till getA() and getB() return
    fmt.Println("Result:", C)
    fmt.Println("All done!")
}

More specifically, I wish Go could handle concurrency behind the scene.

This might be a bit off topic, but I am curious what people think of having such implicit concurrency support. Is it worth to put some effort on it? and what are the potential difficulties and drawbacks?


Edit:

To be clear, the question is not about "what Go is doing right now?", and not even "how it is implemented?" though I appreciate @icza post on what exactly we should expect from Go as it is right now. The question is why it does not or is not capable of returning values, and what are the potential complications of doing that?

Back to my simple example:

   A := go getA()
   B := go getB()

   C := A + B // waits till getA() and getB() return

I do not see any issues concerning the scope of variables, at least from the syntax point of view. The scope of A, B, and C is clearly defined by the block they are living inside (in my example the scope is the main() function). However, a perhaps more legitimate question would be if those variables (here A and B) are "ready" to read from? Of course they should not be ready and accessible till getA() and getB() are finished. In fact, this is all I am asking for: The compiler could implement all the bell and whistles behind the scene to make sure the execution will be blocked till A and B are ready to consume (instead of forcing the programmer to explicitly implement those waits and whistles using channels).

This could make the concurrent programming much simpler, especially for the cases where computational tasks are independent of each other. The channels still could be used for explicit communication and synchronization, if really needed.

like image 633
hagh Avatar asked May 12 '16 18:05

hagh


People also ask

Can a Goroutine return a value?

Channels can be used to fetch return value from a goroutine. Channels provide synchronization and communication between goroutines. You can send the return value in a channel in the goroutine and then collect that value in the main function.

How do you return a value in Golang?

In Go language, you are allowed to return multiple values from a function, using the return statement. Or in other words, in function, a single return statement can return multiple values. The type of the return values is similar to the type of the parameter defined in the parameter list.

Is Goroutine blocked?

When we send data into the channel using a GoRoutine, it will be blocked until the data is consumed by another GoRoutine. When we receive data from channel using a GoRoutine, it will be blocked until the data is available in the channel.

How do I use return in Go?

To declare the named result or return parameters, just use the return type part of the function signature. Below is the general syntax to declare a function in Golang. Syntax to declare a function without named return arguments: func function_name(Parameter-list)(Return_type){ // function body..... }


1 Answers

But this is very easily and idiomatically doable. The language provides the means: Channel types.

Simply pass a channel to the functions, and have the functions send the result on the channel instead of returning them. Channels are safe for concurrent use.

Only one goroutine has access to the value at any given time. Data races cannot occur, by design.

For more, check out the question: If I am using channels properly should I need to use mutexes?

Example solution:

func getA(ch chan int) {
    fmt.Println("getA: Calculating...")
    time.Sleep(300 * time.Millisecond)

    fmt.Println("getA: Done!")
    ch <- 100
}

func getB(ch chan int) {
    fmt.Println("getB: Calculating...")
    time.Sleep(400 * time.Millisecond)

    fmt.Println("getB: Done!")
    ch <- 200
}

func main() {
    cha := make(chan int)
    chb := make(chan int)

    go getA(cha)
    go getB(chb)

    C := <-cha + <-chb // waits till getA() and getB() return
    fmt.Println("Result:", C)
    fmt.Println("All done!")
}

Output (try it on the Go Playground):

getB: Calculating...
getA: Calculating...
getA: Done!
getB: Done!
Result: 300
All done!

Note:

The above example can be implemented with a single channel too:

func main() {
    ch := make(chan int)

    go getA(ch)
    go getB(ch)

    C := <-ch + <-ch // waits till getA() and getB() return
    fmt.Println("Result:", C)
    fmt.Println("All done!")
}

Output is the same. Try this variant on the Go Playground.


Edit:

The Go spec states that the return values of such functions are discarded. More on this: What happens to return value from goroutine.

What you propose bleeds from multiple wounds. Each variable in Go has a scope (in which they can be referred to). Accessing variables do not block. Execution of statements or operators may block (e.g. Receive operator or Send statement).

Your proposal:

go A := getA()
go B := getB()

C := A + B // waits till getA() and getB() return

What is the scope of A and B? 2 reasonable answer would be a) from the go statement or after the go statement. Either way we should be able to access it after the go statement. After the go statement they would be in scope, reading/writing their values should not block.

But then if C := A + B would not block (because it is just reading a variable), then either

a) at this line A and B should already be populated which means the go statement would need to wait for getA() to complete (but then it defeats the purpose of go statement)

b) or else we would need some external code to synchronize but then again we don't gain anything (just make it worse compared to the solution with channels).

Do not communicate by sharing memory; instead, share memory by communicating.

By using channels, it is crystal clear what (may) block and what not. It is crystal clear that when a receive from the channel completes, the goroutine is done. And it gives us the mean to execute the receive whenever we're want to (at the point when its value is needed and we're willing to wait for it), and also the mean to check if the value is ready without blocking (comma-ok idiom).

like image 88
icza Avatar answered Sep 23 '22 12:09

icza