multithreading - Golang 中的 channel 和 mutex 有什么区别?

标签 multithreading go channel

我听说channel优于sycn.Mutex.Lock()当您的程序具有高并发性时。但是为什么 channel 更有效率呢?在我看来,要实现一个安全的缓冲池(我认为 channel 可以被认为是一个缓冲池),你必须使用锁。

如果 channel效率更高,为什么有sycn.Mutex ?因为我可以写下面的代码来模拟sync.Mutex .

type seme struct {
    lock chan int
    locked bool
}


func (l *seme)Lock() {
// state 0 for initial, 1 for locked, 2 for free.
    if atomic.CompareAndSwapInt32(&l.state, 0, 1) {
        l.lock = make(chan int, 1)
    }
    l.lock <- 0
    l.state = 1
}

func (l *seme)UnLock() {
    if !atomic.CompareAndSwapInt32(&l.state, 1, 2)  {
        panic("UnLock a free Lock")
    }
    l.state = 2
    <- l.lock
}

如果 channel到处都比mutex好, 为什么要使用 mutex ?也就是说,什么时候应该使用mutex但不是 channel ?有人可以给我一个例子吗?

最佳答案

channel 从根本上不同于互斥体。

一个有足够细节的正确答案会太长,所以让我们只介绍主要亮点,特别是在 Go channel 方面:

  • Go channel 提供并发例程(goroutines)之间的类型化数据传输。
  • 一个 sync.Mutex为并发例程(goroutines)之间的共享内存提供互斥。

  • 数据传输表示复制某个类型 T 的值。Goroutine A 将一个值放入 channel 中:
    var v T  // v is a value of type T
    ...
    ch <- v  // put v's value into the channel
    

    何时以及是否尝试放置v进入 channel block ,如果你愿意,你可以做些什么,有点复杂,但如果 channel 被缓冲,那么至少一些值可以立即进入它而没有任何阻塞,以便发送 goroutine 可以继续.如果 channel 没有缓冲,发送方会阻塞,直到某个接收方 goroutine 正在积极等待一个值。 (有时这是可取的,有时则不是。)

    同时,goroutine B 从 channel 中取出一个值:
    var w T  // w is also a value of type T
    ...
    w <- ch
    

    要不就:
    w :=<- ch
    

    同样,何时以及是否会阻塞,您可以做什么,何时应该做某事等,可能会变得复杂;但在简单的情况下,这会等待有一个可用的值——让某个 goroutine 执行 ch <- v ,或者如果 channel 被缓冲,则已经完成了 - 然后它复制到变量 w放入 channel 的值。变量 v可能已经改变,甚至在这一点上被完全摧毁。该值已安全存储在 channel 中,现在已从 channel 中删除并放入变量 w .

    Go channel 有一些额外的功能,例如关闭 channel 的能力,它可以防止进一步的写入,并将“数据结束”通知传递给读取操作。这可以通过单值读取 ( w, ok <- ch ) 进行测试,并且在 for w := range ch 中进行了隐式测试。环形。

    一个 sync.Mutex例如,相比之下,您只需调用 LockUnlock .它不保存任何排队的值(如缓冲 channel 那样),甚至也没有防止您意外发送 sync.Mutex 的类型(float 本身除外)。期待string管他呢。这个锁的存在让两个或多个 goroutine 使用共享内存区域来完成某些事情。

    channel 的运行时实现很可能需要某种互斥锁。这不一定是 sync.Mutex本身:任何提供足够互斥的东西就足够了。在 the Go channel implementation you are probably using ,它不是 sync.Mutex而是专门的runtime mutex . (请注意,此链接指向特定行,并且该行可能会随着时间的推移而过时。)由于某些 channel 代码是由编译器本身直接生成的,因此不应假定此处的运行时例程正在使用:您的编译器可能与众不同。然而,研究这个特定的实现可能会让您对您可以使用 channel 做什么有所启发。

    互斥锁通常比 channel 简单得多。要查看示例,请将上述 channel 实现中的代码量(不包括编译器的内联插入)与此特定 Go 实现的 sync.Mutex source code 进行比较。 .

    关于multithreading - Golang 中的 channel 和 mutex 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61420524/

    相关文章:

    rust - 如何在没有不稳定功能或外部包装箱的情况下从 mpsc::channel 读取指定的时间?

    c++ - 如何在不使用 <mutex> 的情况下在 C++11 中实现多线程安全单例

    c# - 在单独的线程中更新 ObservableCollection

    python - 执行外部 python 脚本并获取返回的输出

    c++ - Go 可以在页面级别使用内存吗?

    go - 为什么 channel 不关闭?

    go - Golang 的垃圾 channel ?

    java - 如何在单线程中仅运行 Maven Surefire 插件中的一个 TestNG 类?

    c# - 使用多个任务/消费者使用阻塞集合

    arrays - Golang 将 interface{} 转换为 N 大小的数组