Home  >  Article  >  Backend Development  >  Why are mutexes slower than channels in golang?

Why are mutexes slower than channels in golang?

王林
王林forward
2024-02-09 16:10:18681browse

为什么互斥锁比 golang 中的通道慢?

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.

Question content

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)
    }
}

Workaround

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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete