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

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

互斥锁

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

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

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)
}
所有的努力都值得期许,每一份梦想都应该灌溉!

[转]Go基础之锁的初识的更多相关文章

  1. Go基础之锁的初识

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

  2. 006 01 Android 零基础入门 01 Java基础语法 01 Java初识 06 使用Eclipse开发Java程序

    006 01 Android 零基础入门 01 Java基础语法 01 Java初识 06 使用Eclipse开发Java程序 Eclipse下创建程序 创建程序分为以下几个步骤: 1.首先是创建一个 ...

  3. 005 01 Android 零基础入门 01 Java基础语法 01 Java初识 05 Eclipse简介

    005 01 Android 零基础入门 01 Java基础语法 01 Java初识 05 Eclipse简介 Eclipse是一款集成开发工具--IDE. 集成开发环境(IDE,Integrated ...

  4. 004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构

    004 01 Android 零基础入门 01 Java基础语法 01 Java初识 04 Java程序的结构 Java程序的结构 Java程序外层--类 程序外层,如下面的代码,是一个类的定义. c ...

  5. 003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程

    003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程 Java程序长啥样? 首先编写一个Java程序 记事本编写程序 打开记事本 1.wi ...

  6. 002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介

    002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介 学习Java的基础语法 Java是一门编程语言,学习的逻辑其实和现实世界的语言是一样的,需要了 ...

  7. 001 01 Android 零基础入门 01 Java基础语法 01 Java初识 01 导学

    001 01 Android 零基础入门 01 Java基础语法 01 Java初识 01 导学 welcome to Java World 欢迎来到Java世界 一起领略Java编程世界的奥秘与奥妙 ...

  8. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  9. [转]《Hadoop基础教程》之初识Hadoop

    原文地址:http://blessht.iteye.com/blog/2095675 Hadoop一直是我想学习的技术,正巧最近项目组要做电子商城,我就开始研究Hadoop,虽然最后鉴定Hadoop不 ...

随机推荐

  1. perl学习之正则表达式

    9    Perl 中的正则表达式 正则表达式的三种形式 正则表达式中的常用模式 正则表达式的 8 大原则 正则表达式是 Perl 语言的一大特色,也是 Perl 程序中的一点难点,不过如果大家能够很 ...

  2. python--FTP 上传视频示例

    # 服务端 import json import socket import struct server = socket.socket() server.bind(('127.0.0.1',8001 ...

  3. Python GUI界面开发环境配置:Pycharm+PyQt5

    通过DoS命令行执行如下命令,可能需要管理员权限. 检查Python版本:python 更新pip版本:python -m pip install --upgrade pip 安装PyQt5: pip ...

  4. (转)python之禅

    凡是用过 Python的人,基本上都知道在交互式解释器中输入 import this 就会显示 Tim Peters 的 The Zen of Python,但它那偈语般的语句有点令人费解,所以我想分 ...

  5. STM32L0开发——ADC多通道采集,IDE和IAR开发注意事项

    keil开发L0系列是免费的,官方提供许可的.因此建议Keil开发,L011F3由于flash只有8K,因此不建议HAL库,建议使用cubemx+LL(或snippets库).0.起初,可以参考官方库 ...

  6. PAT Basic 1048

    1048 数字加密 本题要求实现一种数字加密方法.首先固定一个加密用正整数 A,对任一正整数 B,将其每 1 位数字与 A 的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对 13 取余— ...

  7. if-else优化

    过多if-else分支的优化   超过3个就应该去优化,说if-else过多的分支可以使用switch或者责任链模式等等方式来优化.确实,这是一个小问题,不过我们还是可以整理一下这个小问题的重构方式. ...

  8. luogu3396 哈希冲突

    参考这里 我们先预处理模数在 \(\sqrt{n}\) 以内的询问. 要是模数在 \(\sqrt{n}\) 以外,直接暴力统计,反正这样的数又不会超过 \(\sqrt{n}\) 个. 修改的时候也是. ...

  9. 用btrace定位问题

    btrace 截取方法出入参 用btrace的手法,网上有很多.简单记录下我自己的使用经历. btrace脚本: import static com.sun.btrace.BTraceUtils.ex ...

  10. C++枚举类型enum

    为啥需要枚举类型 编程语言中的所有特性都是为了满足某种需求,达到某个目的还出现.不会莫名其妙的出现在那. 枚举可以用来保存一组属性的值.enum的全称是enumeration意思是列举 看着这句话可能 ...