使goroutine同步的方法总结

简介:

使goroutine同步的方法总结

前言:

在前面并发性能对比的文章中,我们可以看到Golang处理大并发的能力十分强劲,而且开发也特别方便,只需要用go关键字即可开启一个新的协程。

但当多个goroutine同时进行处理的时候,就会遇到同时抢占一个资源的情况(并发都会遇到的问题),所以我们希望某个goroutine等待另一个goroutine处理完某一个步骤之后才能继续。sync包就是为了让goroutine同步而出现的。当然还可以使用channel实现,这个后面会介绍到。

锁:

锁有两种:互斥锁(mutex)和读写锁(RWMutex)

互斥锁: 当数据被加锁了之后,除次外的其他协程不能对数据进行读操作和写操作。 这个当然能解决并发程序对资源的操作。但是,效率上是个问题,因为当加锁后,其他协程只有等到解锁后才能对数据进行读写操作。

读写锁: 读数据的时候上读锁,写数据的时候上写锁。有写锁的时候,数据不可读不可写。有读锁的时候,数据可读,不可写。

两种锁的使用方式相同,这里就只列出互斥锁的代码:

 1package main
2
3import (
4  "sync"
5  "time"
6  "fmt"
7)
8
9var num = 0
10
11func main ()  {
12  mu := &sync.Mutex{}
13  for i:=0;i<10000;i++ {
14    go func(){
15      mu.Lock()
16      defer mu.Unlock()
17      num += 1
18    }()
19  }
20  time.Sleep(time.Second)
21  fmt.Println("num:", num)  // 如果不加锁这里的num的值会是一个随机数而不是10000
22}

Once:

有的时候,我们启动多个相同goroutine,但是里面的某个操作我只希望被执行一次,这个时候Once就上场了。

 1package
 1package
2
3
4import (
5  "fmt"
6  "sync"
7  "time"
8)
9
10func main() {
11  var once sync.Once
12  one := func() {
13    fmt.Println("just once")
14  }
15
16  for i := 0; i < 10; i++ {
17    go func(a int) {
18      once.Do(one)   // 只是被执行一次
19    }(i)
20  }
21  time.Sleep(time.Millisecond*200)
22}

WaitGroup:

当某个操作或是某个goroutine需要等待一批goroutine执行完毕以后才继续执行,那么这种多线程(go里面说的线程就是goroutine)等待的问题就可以使用WaitGroup了。

代码如下:

 1package main
2
3import (
4    "sync"
5    "fmt"
6    "time"
7)
8
9var waitGroup sync.WaitGroup
10
11func main () {
12    for i := 0; i < 10; i++ {
13        waitGroup.Add(1)  // 添加需要等待goroutine的数量
14        go func() {
15            fmt.Println("hehe")
16            time.Sleep(time.Second)
17            waitGroup.Done() // 减少需要等待goroutine的数量 相当于Add(-1)
18        } ()
19    }
20
21    waitGroup.Wait()  // 执行阻塞,直到所有的需要等待的goroutine数量变成0
22    fmt.Println("over")
23}

Cond:

sync.Cond是用来控制某个条件下,goroutine进入等待时期,等待信号到来,然后重新启动。

代码如下:

 1package main
2
3import (
4    "fmt"
5    "sync"
6    "time"
7)
8var locker = new(sync.Mutex)
9var cond = sync.NewCond(locker)
10
11func test(x int) {
12    cond.L.Lock() //获取锁
13    cond.Wait()//等待通知 暂时阻塞
14    fmt.Println(x)
15    time.Sleep(time.Second * 1)
16    cond.L.Unlock()//释放锁
17}
18func main() {
19    for i := 0; i < 40; i++ {
20        go test(i)
21    }
22    fmt.Println("start all")
23    time.Sleep(time.Second * 3)
24    fmt.Println("signal1")
25    cond.Signal()   // 下发一个通知随机给已经获取锁的goroutine
26    time.Sleep(time.Second * 3)
27    fmt.Println("signal2")
28    cond.Signal()// 下发第二个通知随机给已经获取锁的goroutine
29    time.Sleep(time.Second * 1)  // 在广播之前要等一会,让所有线程都在wait状态
30    fmt.Println("broadcast")
31    cond.Broadcast()//下发广播给所有等待的goroutine
32    time.Sleep(time.Second * 60)
33}

上面代码有几个要点要特别说明一下:

1. 每个Cond都必须有个与之关联的锁  // 见第9行

2. 协程方法里面一开始/结束都必须加/解锁 // 见第12行和16行

3. cond.Wait()时会自动解锁,当被唤醒时,又会加上锁。所以第2点提到必须加/解锁。

Channel

channel不仅可以用来goroutine之间的通信,也可以使goroutine同步完成协作。这点主要基于从channel取数据的时候,会阻塞当前goroutine这个特性。示例代码如下:

 1package main
2
3import (
4    "fmt"
5    "time"
6)
7
8
9var chan1 = make(chan string512)
10
11var arr1 = []string{"qq","ww","ee","rr","tt"}
12
13func chanTest1() {
14    for _, v := range arr1 {
15        chan1 <- v
16    }
17    close(chan1) // 关闭channel
18}
19
20func chanTest2() {
21    for {
22        getStr, ok := <- chan1  // 阻塞,直到chan1里面有数据
23        if !ok {   // 判断channel是否关闭或者为空
24            return
25        }
26        fmt.Println(getStr) // 按数组顺序内容输出
27    }
28}
29
30func main () {
31    go chanTest1()
32    go chanTest2()
33
34    time.Sleep(time.Millisecond*200)
35}


原文发布时间为:2018-10-8

本文来自云栖社区合作伙伴“Golang语言社区”,了解相关信息可以关注“Golang语言社区”。

相关文章
|
Dubbo Cloud Native Java
重磅下载 | Java 开发者必备手册《Spring Cloud Alibaba 从入门到实战》,阿里双11同款!
Spring Cloud Alibaba 脱胎于阿里中间件团队内部,经受了阿里多年海量业务场景的考验,是目前最成熟、功能最丰富也最有前景的 Spring Cloud 实现。相信在未来 Spring Cloud Alibaba 获得更多开发者的亲睐与应用,这也将成为 Java 开发者必不可少的技能之一。
131491 0
重磅下载 | Java 开发者必备手册《Spring Cloud Alibaba 从入门到实战》,阿里双11同款!
|
13天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
19689 108
|
5天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
4143 7
|
7天前
|
人工智能 安全 API
OpenClaw“小龙虾”进阶保姆级攻略!阿里云/本地部署+百炼API配置+4种Skills安装方法
很多用户成功部署OpenClaw(昵称“小龙虾”)后,都会陷入“看似能用却不好用”的困境——默认状态下的OpenClaw更像一个聊天机器人,缺乏连接外部工具、执行实际任务的能力。而Skills(技能插件)作为OpenClaw的“动手能力核心”,正是打破这一局限的关键:装对Skills,它能帮你自动化处理流程、检索全网资源、管理平台账号,真正变身“能做事的AI管家”。
5018 7
|
9天前
|
人工智能 安全 前端开发
Team 版 OpenClaw:HiClaw 开源,5 分钟完成本地安装
HiClaw 基于 OpenClaw、Higress AI Gateway、Element IM 客户端+Tuwunel IM 服务器(均基于 Matrix 实时通信协议)、MinIO 共享文件系统打造。
7657 5
|
8天前
|
人工智能 API 网络安全
Mac mini × OpenClaw 保姆级配置教程(附阿里云/本地部署OpenClaw配置百炼API图文指南)
Mac mini凭借小巧机身、低功耗和稳定性能,成为OpenClaw(原Clawdbot)本地部署的首选设备——既能作为家用AI节点实现7×24小时运行,又能通过本地存储保障数据隐私,搭配阿里云部署方案,可灵活满足“长期值守”与“隐私优先”的双重需求。对新手而言,无需复杂命令行操作,无需专业技术储备,按本文步骤复制粘贴代码,即可完成OpenClaw的全流程配置,同时接入阿里云百炼API,解锁更强的AI任务执行能力。
6288 2

热门文章

最新文章