zoukankan      html  css  js  c++  java
  • [转]Go基础之锁的初识

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

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

    互斥锁

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

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

    复制代码
    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
    我们修改代码代码需要加锁保护的地方加上锁,并且这里加的是互斥锁,修改后的代码为:

    复制代码
    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
    接下来看读写锁

    读写锁

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

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

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

    复制代码
    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)
    }
    复制代码

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

    复制代码
    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中的原子操作

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

    实例代码:

    复制代码
    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)
    }
    复制代码
    所有的努力都值得期许,每一份梦想都应该灌溉!
  • 相关阅读:
    Mysql登录错误:ERROR 1045 (28000): Plugin caching_sha2_password could not be loaded
    Docker配置LNMP环境
    Docker安装mysqli扩展和gd扩展
    Docker常用命令
    Ubuntu常用命令
    单例模式的优缺点和使用场景
    ABP 多租户数据共享
    ABP Core 后台Angular+Ng-Zorro 图片上传
    ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
    AbpCore 执行迁移文件生成数据库报错 Could not find root folder of the web project!
  • 原文地址:https://www.cnblogs.com/oxspirt/p/8646703.html
Copyright © 2011-2022 走看看