As Ian pointed out, the code you provided has a race condition. Using 
timers to try to avoid a race like you do here may work  *most *of the 
time, but it is not rigorously correct. When dealing with concurrency is is 
critical to always be 100% rigorusly "correct". Otherwise you will 
eventually encounter a situation where you manifest a race and potentially 
produce behavior that can be difficult to replicate and a night mare to 
diagnose. So, to summarize, "close enough" works for some things, but IMHO, 
never for concurrency. 

There are many ways to handle your code correctly in go. Fortunately go 
provides some really reliable and safe ways to handle concurrency. The 
simplest would be a channel:

package main

import (
    "errors"
    "log"
    "time"
)

func main() {
    err := f()
    if err != nil {
        log.Fatal(err)
    }
}

func f() (err error) {

    errorChan := make(chan error)

    go func() {
        errorChan <- longTask()
    }()

    err = <-errorChan
    return err
}

func longTask() error {
    time.Sleep(time.Second * 1)
    return errors.New("long task error")
}
https://play.golang.org/p/2bJeaD7p93K

This should work correctly and is easy to understand and read. Of course, 
in this particular example , there is no real point to even using a 
goroutine.

In your original code, I found it really confusing the way you retunred one 
error, then potentially changed it based on the defer. Thats the kind of 
thing that makes code hard to read, and bugs easy to miss. 


On Saturday, December 7, 2019 at 12:12:43 PM UTC-5, kr c wrote:
>
> package main
>
> import (
>         "errors"
>         "log"
>         "time"
> )
>
> func main() {
>         err := f()
>         if err != nil {
>                 log.Fatal(err)
>         }
> }
>
> func f() (err error) {
>         var rerr error
>
>         defer func() {
>                 if rerr != nil {
>                         err = rerr
>                 }
>         }()
>
>         go func() {
>                 rerr = longTask()
>         }()
>
>         time.Sleep(time.Second * 2)
>         return errors.New("f error")
> }
>
> func longTask() error {
>         time.Sleep(time.Second * 1)
>         return errors.New("long task error")
> }
>
> https://play.golang.org/p/mqpT8XBbeiN
>
> Hi, Everyone! I'm newbie in go. Help needed. 
> As far as I learned, gorutines should pass "value" through channels. 
> There's more safe way to handle this kind of stuffs with using waitgroup, 
> context, channels....I thought, "sharing variable reference in gorutine" is 
> dangerous because of problems like transacion issue. 
>
> But what if that value is just single error? Is it still unsafe or may 
> cause panic? 
>
> "longTask" in examples is a task that I can garantee it will eventually 
> end. 
> I know little about low level programming. So not sure if I can write this 
> kind of code. 
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/9dff0013-65cd-4357-98a6-71c894aa0a52%40googlegroups.com.

Reply via email to