Golang学习笔记之互斥锁(Mutex)

Go语言包中的sync包提供了两种锁,互斥锁(sync.Mutex)和读写锁(sync.RWMutex)
这一篇博文我们只说一下互斥锁。

Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

• 它只有两个公开方法:Lock()加锁,unlock()解锁。
• 在同一个协程中加锁后,不能再继续对其加锁,否则会panic。只有在解锁之后才能再次加锁。
• 只允许只有一个读或者写的场景
• 在使用Unlock()解锁前,未使用Lock()加锁,就会引起一个运行错误。

函数原型
func (m *Mutex) Lock()
Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。
func (m *Mutex) Unlock()
Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。
一个例子理解互斥锁的作用

package main
import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup) {
    num = num + 1
    wg.Done()
}
func main() {
    var w sync.WaitGroup
    for i := 0; i < 1000; i++ {
        w.Add(1)
        //开启协程
        go increment(&w)
    }
    w.Wait()
    fmt.Println("num =", num)
}

在上述程序中,调用1000个协程来进行num=num+1操作
运行几次的输出分别为
num = 971
num = 944
num = 959
每次运行都没有达到预期的效果,因为多个并发的协程试图访问 num 的值,这时就会发生竞态条件。
现在我们可以对上述程序加上锁,每次只能由一个线程来操作num的值

package main

import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
    //互斥锁
    m.Lock()     //当有线程进去进行加锁
    num = num + 1
    m.Unlock()   //出来后解锁,其他线程才可以进去
    wg.Done()
}
var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment(&w, &m)//这里要传引用并不能传值,如果传值,那么每个协程都会得到 Mutex 的一份拷贝,竞态条件还是会发生。
    }
    w.Wait()
    fmt.Println("num =", num)

输出
num = 1000
我们也可以使用缓冲信道来实现互斥锁

func increment2(wg *sync.WaitGroup, b chan bool) {
    //自定义互斥锁
    b <- true
    num = num + 1
    <-b
    wg.Done()
}
func main() {
    var w sync.WaitGroup
    ch := make(chan bool,1)
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment2(&w, ch)
    }
    w.Wait()
    fmt.Println("num =", num)
}

输出
num = 1000

func main() {
    wa := sync.WaitGroup{}

    var mu sync.Mutex
    fmt.Println("加锁0")
    mu.Lock()

    fmt.Printf("上锁中0\t")
    for i := 1; i < 4; i++ {
        wa.Add(1)
        go func(i int) {
            fmt.Printf("加锁%d\t", i)
            mu.Lock()
            fmt.Printf("上锁中%d\t", i)
            time.Sleep(time.Second * 1)
            mu.Unlock()
            fmt.Printf("解锁%d\t", i)
            wa.Done()
        }(i)
    }
    time.Sleep(time.Second * 5)
    mu.Unlock()
    fmt.Println("\n解锁0")

    wa.Wait()
}

输出为
加锁0
上锁中0 加锁2 加锁3 加锁1 上锁中2
解锁0
解锁2 上锁中3 解锁3 上锁中1 解锁1

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容