这个 Go 代码是线程安全的还是我需要一个互斥体?
Is this Go code thread safe or do I need a mutex?
假设我有以下函数,doWork
,它在 goroutine 中开始一些工作,returns 一个 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)
指令进行排序,在这种情况下,我需要一个互斥锁来防止并发 read/writes 出错。
编译器可能不会重新排序赋值和关闭语句,因此如果调用者是 well-behaved 并且按照您的文档的指示进行操作,则不需要互斥体。
它是 thread-safe 仅当您的评论被遵守并且 Err()
在从 Done()
returns.
读取之前永远不会被调用
您可以简单地通过 re-implementing 将 Err()
阻塞为:
func (r *Result) Err() error {
<-r.doneCh
return r.err
}
这将保证 Err()
只有 returns 完成后。鉴于 err 在工作出错之前将为零,您无法判断 Err()
是否成功返回,因为工作已完成,或者因为它尚未完成或出错,除非您首先阻止 Done()
,在这种情况下,为什么不让 Err()
阻塞?
您是否尝试过使用 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")
})
}
假设我有以下函数,doWork
,它在 goroutine 中开始一些工作,returns 一个 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)
指令进行排序,在这种情况下,我需要一个互斥锁来防止并发 read/writes 出错。
编译器可能不会重新排序赋值和关闭语句,因此如果调用者是 well-behaved 并且按照您的文档的指示进行操作,则不需要互斥体。
它是 thread-safe 仅当您的评论被遵守并且 Err()
在从 Done()
returns.
您可以简单地通过 re-implementing 将 Err()
阻塞为:
func (r *Result) Err() error {
<-r.doneCh
return r.err
}
这将保证 Err()
只有 returns 完成后。鉴于 err 在工作出错之前将为零,您无法判断 Err()
是否成功返回,因为工作已完成,或者因为它尚未完成或出错,除非您首先阻止 Done()
,在这种情况下,为什么不让 Err()
阻塞?
您是否尝试过使用 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")
})
}