Go言语并发编程 互斥锁概略

探索 · 2026-07-20 19:38:51

1 、言语互斥锁Mutex

1.1 Mutex引见

Go 言语的并发编程同步对象次要由 sync 包供给  ,互斥锁 (Mutex) 与读写锁 (RWMutex) 就是互斥sync 包中的编制。

互斥锁可以用来呵护一个临界区 ,锁概担保不应时辰只需一个 goroutine 处于该临界区内 。言语次要包含锁定(Lock编制)和解锁(Unlock编制)两个独霸,并发编程起首对进进临界区的互斥goroutine举办锁定,离开时举办解锁 。锁概

独霸互斥锁 (Mutex)时要寄看以下几点 :

  • 不要几回锁定互斥锁 ,言语不然会梗阻 ,并发编程也大年夜大年夜约会招致作古锁(deadlock);
  • 要对互斥锁举办解锁,互斥这也是锁概为了不几回锁定;
  • 不要对未锁定或已解锁的互斥锁解锁;
  • 不要在多个函数之间直接传递互斥锁,sync.Mutex圭表类型属于值圭表类型 ,言语将它传给一个函数时 ,并发编程会产生发火一个副本 ,互斥在函数中对锁的独霸不会影响原锁

总之,一个互斥锁只用来呵护一个临界区,加锁跋文得解锁,对每个锁定独霸 ,都要有且只需一个对应的解锁独霸,也就是加锁和解锁要成对展示 ,最保险的做法时独霸defer语句解锁 。

1.2 Mutex独霸实例

上面的代码模仿取钱和存钱独霸:

package mainimport ( "flag" "fmt" "sync")var (    mutex   sync.Mutex    balance int    protecting uint  // 可否加锁    sign = make(chan struct{ }, 10) //通道,用于等待全数goroutine)// 存钱func deposit(value int) {     defer func() {         sign <- struct{ }{ }    }()    if protecting == 1 {         mutex.Lock()        defer mutex.Unlock()    }    fmt.Printf("余额: %d\n", balance)    balance += value    fmt.Printf("存 %d 后的余额: %d\n", value, balance)    fmt.Println()}// 取钱func withdraw(value int) {     defer func() {         sign <- struct{ }{ }    }()        if protecting == 1 {         mutex.Lock()        defer mutex.Unlock()    }    fmt.Printf("余额: %d\n", balance)    balance -= value    fmt.Printf("取 %d 后的余额: %d\n", value, balance)    fmt.Println()}func main() {         for i:=0; i < 5; i++ {         go withdraw(500) // 取500        go deposit(500)  // 存500    }    for i := 0; i < 10; i++ {   <-sign }    fmt.Printf("往后余额: %d\n", balance)}func init() {     balance = 1000 // 初始账户余额为1000    flag.UintVar(&protecting, "protecting", 0, "可否加锁
,0展示不加锁,1展示加锁")}

上面的代码中,独霸了通道来让主 goroutine 等待其他 goroutine 运转停止,每个子goroutine在运转停止之前向通道发送一个元素 ,主 goroutine 在末尾从这个通道领受元素 ,领受次数与子goroutine个数不异。领受完后就会介入主goroutine 。

代码独霸协程完成多次(5次)对一个账户举办存钱和取钱的独霸 ,先来看不加锁的气候

余额: 1000
存 500 后的余额: 1500

余额: 1000
取 500 后的余额: 1000

余额: 1000
存 500 后的余额: 1500

余额: 1000
取 500 后的余额: 1000

余额: 1000
存 500 后的余额: 1500

余额: 1000
取 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 1000
存 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 1000
存 500 后的余额: 1000

往后余额: 1000

可以看到展示了混乱 ,比如第二次1000的余额取500后仍是1000,这类对不合成本的竞争展示了竞态前提(Race Condition) 。

上面来看加锁的奉行下场:

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

余额: 1000
存 500 后的余额: 1500

余额: 1500
取 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

往后余额: 1000
加锁后就正常了。

上面引见更细化的互斥锁 :读/写互斥锁RWMutex。

2 、读写锁RWMutex

2.1 RWMutex引见

读/写互斥锁RWMutex包含了读锁和写锁 ,分袂对共享成本的“读独霸”和“写独霸”举办呵护 。sync.RWMutex圭表类型中的Lock编制和Unlock编制分袂用于对写锁举办锁定和解锁,而它的RLock编制和RUnlock编制例分袂用于对读锁举办锁定和解锁 。

有了互斥锁Mutex ,为甚么还需求读写锁呢?因为在良多并发独霸中,并发读取占比很除夜 ,写独霸绝对较少,读写锁可以并发读取 ,多么可以供给处事功用 。读写锁具有以下特点:

读写锁读锁写锁
读锁YesNo
写锁NoNo

也就是说  :

  • 假定某个共享成本遭到读锁和写锁呵护时,此外goroutine不克不及举办写独霸 。换句话说就是读写独霸和写写独霸不克不及并行奉行 ,也就是读写互斥;
  • 受读锁呵护时 ,可以同时举办多个读独霸 。

在独霸读写锁时,还需求寄看:

  • 不要对未锁定的读写锁解锁;
  • 对读锁不克不及独霸写锁解锁
  • 对写锁不克不及独霸读锁解锁

2.2 RWMutex独霸实例

改写后面的取钱和存钱独霸,添加查询余额的编制:

package mainimport ( "fmt" "sync")// account 代表计数器。type account struct {  num uint         // 独霸次数 balance int   // 余额 rwMu  *sync.RWMutex // 读写锁}var sign = make(chan struct{ }, 15) //通道,用于等待全数goroutine// 搜检余额
:独霸读锁func (c *account) check() {  defer func() {         sign <- struct{ }{ }    }() c.rwMu.RLock() defer c.rwMu.RUnlock() fmt.Printf("%d 次独霸后的余额: %d\n", c.num, c.balance)}// 存钱	:写锁func (c *account) deposit(value int) {  defer func() {         sign <- struct{ }{ }    }()    c.rwMu.Lock() defer c.rwMu.Unlock()  fmt.Printf("余额: %d\n", c.balance)    c.num += 1    c.balance += value    fmt.Printf("存 %d 后的余额: %d\n", value, c.balance)    fmt.Println() }// 取钱:写锁func (c *account) withdraw(value int) {     defer func() {         sign <- struct{ }{ }    }() c.rwMu.Lock() defer c.rwMu.Unlock()    fmt.Printf("余额: %d\n", c.balance)      c.num += 1    c.balance -= value fmt.Printf("取 %d 后的余额: %d\n", value, c.balance)    fmt.Println()  }func main() {  c := account{ 0, 1000, new(sync.RWMutex)} for i:=0; i < 5; i++ {         go c.withdraw(500) // 取500        go c.deposit(500)  // 存500  go c.check()    }    for i := 0; i < 15; i++ {   <-sign } fmt.Printf("%d 次独霸后的余额: %d\n", c.num, c.balance)}

奉行下场  :

余额: 1000
取 500 后的余额: 500

1 次独霸后的余额: 500
1 次独霸后的余额: 500
1 次独霸后的余额: 500
1 次独霸后的余额: 500
1 次独霸后的余额: 500
余额: 500
存 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

余额: 1000
存 500 后的余额: 1500

余额: 1500
取 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

余额: 1000
取 500 后的余额: 500

余额: 500
存 500 后的余额: 1000

10 次独霸后的余额: 1000

读写锁和互斥锁的不合的处地址于读写锁把对共享成本的读独霸和写独霸离开了,可以完成更宏壮的访谒独霸。

总结:

读写锁也是一种互斥锁,它是互斥锁的扩大年夜大年夜大年夜大年夜。在独霸时需求寄看 :

  • 加锁后必定要解锁
  • 不要几回加锁或解锁
  • 不解锁未锁定的锁
  • 不要传递互斥锁

到此这篇关于Go言语并发编程 互斥锁概略的文章就引见到这了,更多相干Go言措辞语并发编程 互斥锁内容请搜刮完竣下载之前的文章或延续不雅不雅不雅不雅鉴赏上面的相干文章希看大年夜师往后多多支撑完竣下载!

文章推荐:

七雄纷争手游最全武将搭配攻略

8000字干货!7大方面帮你完全掌握应用图标设计

做好网站优化是上排名的关键

线上营销推广时代,企业网站优化的突围之道

全平易近奇不雅不雅不雅不雅不雅不雅2神明使者义务若何做

王者名望张良千筹问战皮肤返点券收入编制

哈利波特魔法省悟禁林查询访谒秘鲁毒牙龙打法攻略

部落与弯刀宏除夜熔岩浮泛职位攻略

冰汽时代手游若何设置中文

SEO内容优化 :文章怎样编辑才是高质量的

堆叠除夜陆泥土掉落踪掉落踪编制除夜全

光遇6.23每日义务攻略2022