假设我有以下函数doWork
,它在goroutine中启动一些工作并返回Result
以检查完成和错误:
func doWork() *Result {
r := Result{doneCh: make(chan struct{})}
go func() {
var err error
defer func() {
r.err = err
close(r.doneCh)
}()
// do some work
}()
return &r
}
其中Result
是以下结构:
type Result struct {
doneCh chan struct{}
err error
}
// doneCh returns a closed chan when the work is done.
func (r *Result) Done() <-chan struct{} {
return r.doneCh
}
// Err returns a non-nil err if the work failed.
// Don't call Err until Done returns a closed chan.
func (r *Result) Err() error {
return r.err
}
如果我在关闭err
之前设置doneCh
:,那么是否可以安全使用此代码线程
defer func() {
r.err = err
close(r.doneCh)
}()
或编译器是否可以随意订购r.err = err
和close(r.doneCh)
指令,在这种情况下,我需要一个互斥锁来防止错误时发生并发读/写。
答案 0 :(得分:3)
只有遵守您的意见并且在Err()
的读取返回之前永远不会调用Done()
,它才是线程安全的。
您可以通过重新实现它来简单地阻止Err()
:
func (r *Result) Err() error {
<-r.doneCh
return r.err
}
这将保证Err()
仅在完成后才返回。鉴于错误将在工作错误之前为零,您无法告知Err()
是否因为工作已完成或因为尚未完成或错误而成功返回,除非您先阻止Done()
,在这种情况下,为什么不只是让Err()
阻止?
答案 1 :(得分:1)
编译器可能不会重新排序赋值和关闭语句,因此如果调用者表现良好并且按照文档的指示执行操作,则不需要互斥锁。
答案 2 :(得分:0)
您是否尝试过使用chan error
并测试频道是否在接收时打开或关闭?
package main
import (
"errors"
"fmt"
)
func delegate(work func(ch chan error)) {
ch := make(chan error)
go work(ch)
for {
err, opened := <- ch
if !opened {
break
}
// Handle errors
fmt.Println(err)
}
}
func main() {
// Example: error
delegate(func(ch chan error) {
defer close(ch)
// Do some work
fmt.Println("Something went wrong.")
ch <- errors.New("Eyyyyy")
})
// Example: success
delegate(func(ch chan error) {
defer close(ch)
// Do some work
fmt.Println("Everything went fine.")
})
// Example: error
delegate(func(ch chan error) {
defer close(ch)
// Do some work
fmt.Println("Something went wrong more than once.")
ch <- errors.New("Eyyyyy 1")
ch <- errors.New("Eyyyyy 2")
ch <- errors.New("Eyyyyy 3")
})
}