0
点赞
收藏
分享

微信扫一扫

学到了!将缓冲 channel 当做 Mutex 来使用



via:
​​​https://mipsmonsta.medium.com/golang-using-buffered-channel-like-a-mutex-9c7c80ec5c27​​作者:Mipsmonsta

大家好,我是四哥。

原文如下:

作为 Go 官方包的一部分,sync 包有下面这段声明:

sync 包提供了基本的同步原语,例如互斥锁。除了 Once 和 WaitGroup 类型之外,大多数其他类型都是为底层函数库准备的。通过 channel 和通信更好地完成更高级别的同步.

在你能找到的关于允许并发访问的绝大多数例子中,很多都是使用互斥锁来解决问题。然而,几乎很少有示例给我们展示如何使用 channel 提供同步机制。所以,这篇文章我们就来讨论下。

互斥锁的特性

为了使互斥锁起作用,访问共享变量时需要加锁,操作完成之后需要解锁。相同的互斥锁不允许多次加锁,以免出现竞态条件。

无缓冲 channel 及其不足之处

如果没有接收方,发送者将会阻塞;相同地,如果没有发送方,接收者将会阻塞。基于这种特性,所以我们不能将无缓冲的 channel 作为锁来使用。

我们来看看缓冲 channel 是否可以当做互斥锁来使用。

缓冲为 1 的 channel 的特性及其可取之处

缓冲大小为 1 的 channel 具有如下的特性:如果缓冲满了,发送时将会阻塞;如果缓存腾空,发送时就会解除阻塞。

显然,这种 channel 的阻塞特性是可取的,与互斥锁的特性做个对比:

缓冲满时 <--> 上锁

缓冲腾空 <--> 解锁

我们一起通过代码演示下这种特性。

演示:如何将缓冲 channel 作为 “锁” 来使用

我们假设有一列名字需要写入到文件中,每个名字需要连续写 1000 次,且不允许不同名字出现交叉情况。

package main
import (
"errors"
"fmt"
"os"
"sync"
)
func main() {
file, err := os.Create("record.txt")

defer func() {
if err := recover(); err != nil {
fmt.Printf("Error encounter: %w", err)
}
file.Close()
}()
if err != nil {
panic(errors.New("Cannot create/open file"))
}
ss := []string{ //string slice literals
"James",
"Avery",
"Peter",
"John",
"Beau",
}
chanLock := make(chan int, 1) //1
var wg sync.WaitGroup
for _, str := range ss { //2
wg.Add(1) //amended thanks to response from Wang
//Sheng
go func(aString string) {

chanLock <- 1 //3
for i := 0; i < 1000; i++ {
file.WriteString(aString + "\n")
}
<-chanLock //4
wg.Done() //5
}(str) //pass by value
}
wg.Wait()
}

上面的代码中,//1 我们创建了缓冲为 1 的 channel。//2 我们创建了个数与名字数量相同的 goroutine。//3 相当于加锁,//4 相当于解锁,这样就实现了多 goroutine 之间同步地将名字写入到 record.txt 文件,但每次只会有一个 goroutine 操作该文件。

需要注意的是,我们通过 WaitGroup 来保证子 goroutine 完成任务之前,主协程不会退出。

希望这篇文章对你有帮助,enjoy coding!

2022 必读 Go 图书推荐

对了,看完文章,记得点击下方的卡片。关注我哦~ 👇👇👇

这是持续翻译的第 27/100 篇优质文章。
如果你有想交流的话题,欢迎留言。

如果您的朋友也在学习 Go 语言,相信这篇文章对 TA 有帮助,欢迎转发分享给 TA,非常感谢!

学到了!将缓冲 channel 当做 Mutex 来使用_编程语言

举报

相关推荐

0 条评论