Why are mutexes slower than channels in golang? This is a common problem and many developers are exploring the cause of this issue. Mutexes and channels are commonly used synchronization mechanisms in golang, and they play an important role in concurrent programming. However, sometimes we find that the performance of mutexes is worse than channels. Why is this? PHP editor Youzi will answer this question for everyone in this article to help readers better understand the performance differences in concurrent programming.
I'm making a program that crawls websites and returns their status.
I wrote this program in a different way. The first uses a mutex to prevent concurrent writes to the map so that I can get rid of data races. Then for the same purpose I implemented it using channels. But when I benchmarked it, I realized that implementing it using channels is much faster than implementing mutexes. I want to know why this happens? Why do mutexes lack performance? Am I doing something wrong with the mutex?
Benchmark results:
Code
package concurrency import "sync" type websitechecker func(string) bool type result struct { string bool } func checkwebsites(wc websitechecker, urls []string) map[string]bool { results := make(map[string]bool) var wg sync.waitgroup var mu sync.mutex for _, url := range urls { wg.add(1) go func(u string) { defer wg.done() mu.lock() results[u] = wc(u) mu.unlock() }(url) } wg.wait() return results } func checkwebsiteschannel(wc websitechecker, urls []string) map[string]bool { results := make(map[string]bool) resultchannel := make(chan result) for _, url := range urls { go func(u string) { resultchannel <- result{u, wc(u)} }(url) } for i := 0; i < len(urls); i++ { r := <-resultchannel results[r.string] = r.bool } return results }
Test code
package concurrency import ( "reflect" "testing" "time" ) func mockWebsiteChecker(url string) bool { time.Sleep(20 * time.Millisecond) if url == "https://localhost:3000" { return false } return true } func TestCheckWebsites(t *testing.T) { websites := []string{ "https://google.com", "https://localhost:3000", "https://blog.gypsydave5.com", } want := map[string]bool{ "https://google.com": true, "https://blog.gypsydave5.com": true, "https://localhost:3000": false, } got := CheckWebsites(mockWebsiteChecker, websites) if !reflect.DeepEqual(got, want) { t.Errorf("got %v, want %v", got, want) } } func BenchmarkCheckWebsites(b *testing.B) { urls := make([]string, 1000) for i := 0; i < len(urls); i++ { urls[i] = "a url" } b.ResetTimer() for i := 0; i < b.N; i++ { CheckWebsites(mockWebsiteChecker, urls) } } func BenchmarkCheckWebsitesChannel(b *testing.B) { urls := make([]string, 1000) for i := 0; i < len(urls); i++ { urls[i] = "a url" } b.ResetTimer() for i := 0; i < b.N; i++ { CheckWebsitesChannel(mockWebsiteChecker, urls) } }
It seems to me that with a mutually exclusive version of the code you not only protect the results
mapping but also the wc
(The call can only be made after acquiring the lock, so you can effectively serialize the call). Sends to chan will lock the channel only when the right side is ready, so calls to wc
can occur simultaneously. See if the code looks like
go func(u string) { defer wg.Done() r := wc(u) mu.Lock() results[u] = r mu.Unlock() }(url)
Using mutex has better performance.
The above is the detailed content of Why are mutexes slower than channels in golang?. For more information, please follow other related articles on the PHP Chinese website!