go 并发 gorouting chan channel select Mutex sync.One

news/2025/2/22 11:04:55

goroutine

// head: 前缀  index:是一个int的指针
func print(head string, index *int) {
    for i := 0; i < 5; i++ {
       // 指针对应的int ++
       *index++
       fmt.Println(*index, head, i)
       // 暂停1s
       time.Sleep(1 * time.Second)
    }
}

/*
Go 允许使用 go 语句开启一个新的运行期线程,即 goroutine
以一个不同的、新创建的 goroutine 来执行一个函数
同一个程序中的所有 goroutine 共享同一个地址空间。
*/
func main() {
    fmt.Println("main ...")
    index := 0
    go print("first", &index)
    go print("second", &index)
    time.Sleep(6 * time.Second)
    fmt.Println("success ...")
}

chan 一般用法

// 求和,并将数据放在channel中
func sum(arr []int, resultChan chan int) {
    if len(arr) <= 0 {
       resultChan <- 0
       return
    }

    var sum = 0
    for _, value := range arr {
       sum += value
    }

    fmt.Println(sum)

    // 将结果放在channel中
    resultChan <- sum
}

/*
channel 用于 goroutine之间进行通信
 1. 创建channel
    ch1 := make(chan 类型)  默认是没有缓冲区的
    ch2 := make(chan 类型, 缓存长度)
 2. 添加数据到channel中
    ch1 <- 123
 3. 从channel中获取数据
    var value = <- ch1

有无缓冲区区别:
1. 没有缓冲区 按照缓冲区为1来处理,即channel只能放一个数据
2. channel满了后就会阻塞,直到有空位才可以继续放入数据
3. 获取数据类似,阻塞到channel中有数据
*/
func main() {
    fmt.Println("main ...")
    // 创建一个没有缓冲区的channel
    resultChan := make(chan int)
    array1 := []int{10, 20, 30}
    array2 := []int{1, 2, 3}
    // 给两个数组求和并将结果放在channel中
    go sum(array1, resultChan)
    go sum(array2, resultChan)
    // 从channel中获取两个数据,打印到console
    fmt.Println(<-resultChan, <-resultChan)
    fmt.Println("continue ...")
    // 如果继续获取则会报错:fatal error: all goroutines are asleep - deadlock!
    // fmt.Println(<-resultChan)
    fmt.Println("success ...")
}

无缓冲区的chan只能结合goroutine使用

func main() {
    fmt.Println("main ...")
    // 创建一个没有缓冲区的channel
    resultChan := make(chan int)
    // chan 只能结合goroutine来使用,否则报错
    // fatal error: all goroutines are asleep - deadlock!
    resultChan <- 10
    fmt.Println(<-resultChan)
    fmt.Println("success ...")
}

有缓冲区的chan可直接赋值

func main() {
    fmt.Println("main ...")
    // 创建一个有缓冲区的channel
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println("success ...")
}

for rang获取channel数据

func addData(ch chan int, len int) {
    for i := 0; i < len; i++ {
       ch <- i
    }
    // 如果不关闭,for val := range ch 会阻塞获取数据
    close(ch)
}

/*
  - 可以使用rang遍历channel,如果channel关闭则直接结束,否则会阻塞等待数据的输入
    for val := range ch
*/
func main() {
    fmt.Println("main ...")
    // 创建一个有缓冲区的channel
    len := 5
    ch := make(chan int, len)
    go addData(ch, len)
    for val := range ch {
       fmt.Println(val)
    }
    fmt.Println("success ...")
}

select **等待多个goroutine

select可以等待多个goroutine,会阻塞一直到某个case不在阻塞。

func print1(header string, ch1, ch2 chan int) {
    for i := 0; i < len; i++ {
       select {
          case val := <-ch1:
             fmt.Println(header, val)
             time.Sleep(time.Second)
          case ch2 <- i:
             // do nothing
       }
    }
}

func main() {
    fmt.Println("main ...")
    ch1 := make(chan int)
    ch2 := make(chan int)
    go print1("f1", ch1, ch2)
    go print1("f2", ch2, ch1)
    time.Sleep(6 * time.Second)
    fmt.Println("success ...")
}

WaitGroup等待所有goroutine完成

类似java中的CountDownLatch

// 不怎么理解为什么group要用指针
func work(index int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println(index)
    time.Sleep(time.Second)
}

func main() {
    fmt.Println("main ...")

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
       wg.Add(1)
       // 为什么要将wg的指针传过去
       go work(i, &wg)
    }

    // 阻塞到所有的goroutine完成后
    wg.Wait()

    fmt.Println("success ...")
}

并发锁Mutex

type ConcurrentMap struct {
    lock    sync.Mutex
    hashmap map[string]int
}

// 1. cm *ConcurrentMap 要传指针,否则操作的是副本
// 2. wg *sync.WaitGroup 这个也传指针,确保操作的是一个对象
func (cm *ConcurrentMap) inc(index int, key string, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println(index, "start")
    cm.lock.Lock()
    before := cm.hashmap[key]
    fmt.Println(index, "before", before)
    cm.hashmap[key] = cm.hashmap[key] + 1
    time.Sleep(time.Microsecond * 100)
    after := cm.hashmap[key]
    fmt.Println(index, "after", after)
    if before+1 != after {
       fmt.Println(index, "error >>>>>")
    }
    cm.lock.Unlock()
    fmt.Println(index, "end")
}

func main() {
    fmt.Println("main ...")

    cm := ConcurrentMap{hashmap: make(map[string]int)}
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
       wg.Add(1)
       go cm.inc(i, "apple", &wg)
    }
    wg.Wait()
    fmt.Println("success ...", cm.hashmap["apple"])
}

RWMutex 读写锁子

参考:

https://www.jianshu.com/p/679041bdaa39

sync.Once 配置文件只加载一次

需求:获取配置文件,如果没有价值只就加载

写法1:

func initMap() {
    if hasInitMap {
       return
    }
    initMapLock.Lock()
    defer initMapLock.Unlock()
    if !hasInitMap {
       fmt.Println("init map")
       m = map[string]string{
          "aaa": "111",
          "bbb": "22",
       }
       hasInitMap = true
    }
}

func getValue1(key string) string {
    initMap()
    return m[key]
}

写法2:

// 定义一次执行对象
var once sync.Once

func initMap2() {
    m = map[string]string{
       "aaa": "111",
       "bbb": "22",
    }
}

func getValue2(key string) string {
    // 一次执行
    once.Do(initMap2)
    return m[key]
}

func main() {
    fmt.Println("main ...")
    for i := 0; i < 20; i++ {
       fmt.Println(getValue1("aaa"))
       fmt.Println(getValue2("aaa"))
    }
    fmt.Println("success ...")
}

sync.Map 类型ConcurrentHashMap

是安全的Map

atomic.AddInt64(&intV,1) 对基础类型安全操作方法

多线程给变量递增: intV := 3

1. 直接+1 线程不安全

2. 使用Mutex锁代价太大

3. 使用atomic包的方法最好,类似Java中的Atomic

参考

https://blog.csdn.net/weixin_53623989/article/details/136209823

https://blog.csdn.net/e2788666/article/details/130644433


http://www.niftyadmin.cn/n/5862155.html

相关文章

Unity教程(二十一)技能系统 基础部分

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

GPS定位上NMEA和CASIC协议的区别

NMEA和CASIC是两个不同领域的协议&#xff0c;它们各自具有独特的特点和应用范围。以下是两者的主要区别&#xff1a; 一、定义与背景 NMEA协议 定义&#xff1a;NMEA&#xff08;National Marine Electronics Association&#xff09;是一个为航海电子设备制定通信协议的组织…

(蓝桥杯——10. 小郑做志愿者)洛斯里克城志愿者问题详解

题目背景 小郑是一名大学生,她决定通过做志愿者来增加自己的综合分。她的任务是帮助游客解决交通困难的问题。洛斯里克城是一个六朝古都,拥有 N 个区域和古老的地铁系统。地铁线路覆盖了树形结构上的某些路径,游客会询问两个区域是否可以通过某条地铁线路直达,以及有多少条…

多对二硫键成环技术

蛋白质和多肽类药物具有作用位点专一&#xff0c;疗效明确等优点&#xff0c;近年来&#xff0c;蛋白质和多肽类药物的研究和发展已经成为生物医药领域研究的一个热点。二硫键在维持多肽和蛋白质的空间立体结构及由此决定的生物活性中发挥着重要的作用。二硫键即为蛋白质或多肽…

33. 搜索旋转排序数组(LeetCode热题100)

题目来源&#xff1a; 33. 搜索旋转排序数组 - 力扣&#xff08;LeetCode&#xff09; 代码实现&#xff1a; class Solution { public:int search(vector<int>& nums, int target) {//闭区间写法int nnums.size();int left0,rightn-1;while(left<right){int m…

Git笔记汇总,持续更新~

Git 是一个广泛使用的分布式版本控制系统&#xff0c;以下是一些常用 Git 命令的详细介绍&#xff1a; 仓库操作 1. git init 功能&#xff1a;在当前目录下初始化一个新的 Git 仓库。用法&#xff1a; git init示例&#xff1a;在 my_project 目录下初始化一个新的 Git 仓…

C++ 设计模式-策略模式

支付策略 #include <iostream> #include <memory> #include <unordered_map> #include <vector> #include <ctime>// 基础策略接口 class PaymentStrategy { public:virtual ~PaymentStrategy() default;virtual std::string name() const 0;…

Linux驱动开发之音频驱动与基础应用编程

目录 CODEC芯片 音频编码 I2S总线接口 数字音频接口(DAI) 设备树配置 ALSA 音频相关概念 应用程序编写 运行测试 CODEC芯片 音频若想被CPU“听到”&#xff0c;就必须转换为CPU能够“听懂”的语言&#xff0c;即二进制数据的0和1。在信号处理领域&#xff0c;声音是模…