前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go基础之锁的初识

Go基础之锁的初识

作者头像
coders
发布2018-03-30 12:16:29
4990
发布2018-03-30 12:16:29
举报
文章被收录于专栏:coder修行路coder修行路

当我们的程序就一个线程的时候是不需要用到锁的,但是通常我们实际的代码不会是单个线程的,所有这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢?

  • 当我们多个线程在读相同的数据的时候则是需要加锁的
  • 当我们的程序既有读又有写的时候更是需要加锁的
  • 当我们有多个线程在写的时候同样也是需要加锁

互斥锁

互斥锁:同一个时刻只有一个线程能够拿到锁

我们先通过一个例子来演示,如果当多个线程同时更改一个变量,结果会是怎么样 不加锁版本

代码语言:javascript
复制
package main

import (
    "sync"
    "fmt"
)

var (
    //lock sync.Mutex
    count int
    w sync.WaitGroup  //用于等待子线程执行完之后退出
)

func main() {
    w.Add(1) // 在调用线程前执行w.add
    go func(){
        for i:=0;i<100000;i++{
            count++
        }
        w.Done()  //执行完 执行w.Done
    }()
    for i :=0;i<100000;i++{
        count++
    }
    w.Wait() // 最后执行w.wait等待所有的线程执行完毕
    fmt.Println(count)

}

当我们运行多次就可以发现,最后的结果基本不可能是我们先看到的:200000 我们修改代码代码需要加锁保护的地方加上锁,并且这里加的是互斥锁,修改后的代码为:

代码语言:javascript
复制
package main

import (
    "sync"
    "fmt"
)

var (
    lock sync.Mutex
    count int
    w sync.WaitGroup  //用于等待子线程执行完之后退出
)

func main() {
    w.Add(1) // 在调用线程前执行w.add
    go func(){
        for i:=0;i<100000;i++{
            lock.Lock()
            count++
            lock.Unlock()
        }
        w.Done()  //执行完 执行w.Done
    }()
    for i :=0;i<100000;i++{
        lock.Lock()
        count++
        lock.Unlock()
    }
    w.Wait() // 最后执行w.wait等待所有的线程执行完毕
    fmt.Println(count)

}

这次当我们多次运行的时候,就能保证我们每次都能看到我们想要的值:200000 接下来看读写锁

读写锁

读写锁主要用到读多写少的场景 读写锁分为:读锁和写锁

如果自己设置了一个写锁,那么其他读的线程以及写的线程都拿不到锁,这个时候和互斥锁的功能相同 如果自己设置了一个读锁,那么其他写的线程是拿不到锁的,但是其他读的线程都是可以拿到这个锁

我们把上面的例子代码进行更改:

代码语言:javascript
复制
package main

import (
    "sync"
    "fmt"
)    

var (
    rwlock sync.RWMutex
    w sync.WaitGroup
    count int
)


func main() {
    w.Add(1)
    go func(){
        for i:=0;i<1000000;i++{
            rwlock.Lock() // 这里定义了一个写锁
            count++
            rwlock.Unlock()
        }
        w.Done()
    }()

    for i:=0;i<1000000;i++{
        rwlock.Lock() // 这里定义了一个写锁
        count++
        rwlock.Unlock()
    }
    w.Wait()
    fmt.Println(count)
}

通过设置写锁,我们同样可以实现数据的一致性 下面是一个读锁的使用例子:

代码语言:javascript
复制
package main

import (
    "sync"
    "fmt"
)

var (
    rwlock sync.RWMutex
    w sync.WaitGroup
    count int
)


func main() {
    w.Add(1)
    go func(){
        for i:=0;i<1000000;i++{
            rwlock.Lock() // 这里定义了一个写锁
            count++
            rwlock.Unlock()
        }
        w.Done()
    }()

    for i:=0;i<16;i++{
        w.Add(1)
        go func(){
            rwlock.RLock() //这里定义了一个读锁
            fmt.Println(count)
            rwlock.RUnlock() //释放读锁
            w.Done()
        }()
    }
    w.Wait()
    fmt.Println(count)
}

Go中的原子操作

原子操作,我们则不需加锁,也能保证数据的一致性 并且如果只是计算,那么原子操作则是最快的

实例代码:

代码语言:javascript
复制
package main

import (
    "sync"
    //"time"
    "sync/atomic"
    "fmt"
)

var (
    w sync.WaitGroup
    count int32
)


func main() {
    w.Add(1)
    //start := time.Now().UnixNano()
    go func() {
        for i:=0;i<1000000;i++{
            atomic.AddInt32(&count,1)
        }
        w.Done()
    }()

    for i:=0;i<1000000;i++{
        atomic.AddInt32(&count,1)
    }
    w.Wait()
    //end := time.Now().UnixNano()
    //fmt.Println((end- start)/1000/1000)
    fmt.Println(count)
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-03-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 互斥锁
  • 读写锁
  • Go中的原子操作
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档