我已经完成了简单的基准测试,在传递消息和锁定共享值方面效率更高。
首先,请检查以下代码。
package main import ( "flag" "fmt" "math/rand" "runtime" "sync" "time" ) type Request struct { Id int ResChan chan Response } type Response struct { Id int Value int } func main() { procNum := flag.Int("proc", 1, "Number of processes to use") clientNum := flag.Int("client", 1, "Number of clients") mode := flag.String("mode", "message", "message or mutex") flag.Parse() if *procNum > runtime.NumCPU() { *procNum = runtime.NumCPU() } fmt.Println("proc:", *procNum) fmt.Println("client:", *clientNum) fmt.Println("mode:", *mode) runtime.GOMAXPROCS(*procNum) rand.Seed(time.Now().UnixNano()) var wg sync.WaitGroup sharedValue := 0 start := time.Now() if *mode == "message" { reqChan := make(chan Request) // increasing channel size does not change the result go func() { for { req := <-reqChan sharedValue++ req.ResChan <- Response{Id: req.Id, Value: sharedValue} } }() for i := 0; i < *clientNum; i++ { wg.Add(1) go func(index int) { defer wg.Done() c := make(chan Response) defer close(c) id := rand.Int() reqChan <- Request{Id: id, ResChan: c} <-c }(i) } } else if *mode == "mutex" { mutex := &sync.Mutex{} for i := 0; i < *clientNum; i++ { wg.Add(1) go func(index int) { defer wg.Done() mutex.Lock() sharedValue++ mutex.Unlock() }(i) } } wg.Wait() elapsed := time.Since(start) fmt.Println("Elapsed:", elapsed, "value:", sharedValue) }
您已经注意到,程序相对简单。在 消息模式下 ,它通过消息传递来增加sharedValue。在 互斥锁模式下 ,它通过锁定来增加 sharedValue 。
我尝试只将一个频道用于 消息模式 并放弃了。我认为这不可能,不是吗?
我的计算机有2个Xeon CPU,每个都有6个内核。由于超线程,逻辑上有24个内核可用。它的内存大小是12G。
如果我用任意数量的标志运行程序,则 互斥锁模式 总是至少快2倍(通常是3倍)。
好的,我可以理解,管理渠道需要一定的成本。然后,如果仅考虑性能,是否有任何理由使用通道而不是互斥锁?另外,巨大的消息量可以忽略消息传递成本吗?
如果仅考虑性能,是否有任何理由使用通道而不是互斥锁?
并不是的。维基页面“ 使用一个sync.Mutex或一个频道? ”说使用最富表现力和/或最简单的那个。 有一个用于Mutex的通道示例,但已注释如下:
sync.Mutex
尽管通道为受保护的数据提供了很好的解决方案,但是对于一个作者和许多读者来说,它的效率较低。
该线程添加:
如果您要共享数据,并且从不阻塞锁定部分,请使用互斥锁。 在非阻塞情况下,互斥对象确实很便宜 。 如果您有一些共享服务执行某些复杂或较长的工作,并且必须对其进行序列化,请考虑为其提供一个自己的goroutine,该例程可接收来自通道的请求并在完成后将回复发送回去。通常,您发送struct带有输入参数的a和用于回复的通道对象。 这很像RPC。 通道用于通信 ,而不是锁定。 如果仅出于锁定目的而通过通道发送无意义的数据,则可能会使事情复杂化。
如果您要共享数据,并且从不阻塞锁定部分,请使用互斥锁。 在非阻塞情况下,互斥对象确实很便宜 。
如果您有一些共享服务执行某些复杂或较长的工作,并且必须对其进行序列化,请考虑为其提供一个自己的goroutine,该例程可接收来自通道的请求并在完成后将回复发送回去。通常,您发送struct带有输入参数的a和用于回复的通道对象。 这很像RPC。
struct
通道用于通信 ,而不是锁定。 如果仅出于锁定目的而通过通道发送无意义的数据,则可能会使事情复杂化。