当前位置: 首页 > news >正文

go 并发 gorouting chan channel select Mutex sync.One

goroutine

// head: 前缀  index:是一个int的指针
func print(head string, index *int) {for i := 0; i < 5; i++ {// 指针对应的int ++*index++fmt.Println(*index, head, i)// 暂停1stime.Sleep(1 * time.Second)}
}/*
Go 允许使用 go 语句开启一个新的运行期线程,即 goroutine
以一个不同的、新创建的 goroutine 来执行一个函数
同一个程序中的所有 goroutine 共享同一个地址空间。
*/
func main() {fmt.Println("main ...")index := 0go 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 <- 0return}var sum = 0for _, value := range arr {sum += value}fmt.Println(sum)// 将结果放在channel中resultChan <- sum
}/*
channel 用于 goroutine之间进行通信1. 创建channelch1 := make(chan 类型)  默认是没有缓冲区的ch2 := make(chan 类型, 缓存长度)2. 添加数据到channel中ch1 <- 1233. 从channel中获取数据var value = <- ch1有无缓冲区区别:
1. 没有缓冲区 按照缓冲区为1来处理,即channel只能放一个数据
2. channel满了后就会阻塞,直到有空位才可以继续放入数据
3. 获取数据类似,阻塞到channel中有数据
*/
func main() {fmt.Println("main ...")// 创建一个没有缓冲区的channelresultChan := make(chan int)array1 := []int{10, 20, 30}array2 := []int{1, 2, 3}// 给两个数组求和并将结果放在channel中go sum(array1, resultChan)go sum(array2, resultChan)// 从channel中获取两个数据,打印到consolefmt.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 ...")// 创建一个没有缓冲区的channelresultChan := make(chan int)// chan 只能结合goroutine来使用,否则报错// fatal error: all goroutines are asleep - deadlock!resultChan <- 10fmt.Println(<-resultChan)fmt.Println("success ...")
}

有缓冲区的chan可直接赋值

func main() {fmt.Println("main ...")// 创建一个有缓冲区的channelch := make(chan int, 2)ch <- 1ch <- 2fmt.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 ...")// 创建一个有缓冲区的channellen := 5ch := 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.WaitGroupfor 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.Mutexhashmap 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] + 1time.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.WaitGroupfor 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.Oncefunc 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.mrgr.cn/news/91857.html

相关文章:

  • 【OS安装与使用】part6-ubuntu 22.04+CUDA 12.4运行MARL算法(多智能体强化学习)
  • DDD架构实战:用Java实现一个电商订单系统,快速掌握领域驱动设计
  • 一文详解U盘启动Legacy/UEFI方式以及GPT/MBR关系
  • 【工具篇】【深度解析 DeepAI 工具:开启 AI 应用新体验】
  • RNN中远距离时间步梯度消失问题及解决办法
  • Linux----线程
  • 《Keras 3 :使用 Vision Transformers 进行物体检测》:此文为AI自动翻译
  • 《Keras 3 : 使用迁移学习进行关键点检测》:此文为AI自动翻译
  • IO模型与NIO基础--NIO网络传输选择器--字符编码
  • 代码随想录算法训练营第四十五天| 动态规划08
  • JavaScript变量的作用域介绍
  • nodejs运行的坎坷之路
  • 《论系统需求分析方法》写作心得 - 系统分析师
  • 业务流程中的流程管理
  • STL —— 洛谷字符串(string库)入门题(蓝桥杯题目训练)(二)
  • 企业组网IP规划与先关协议分析
  • 第一个CMAKE项目hello cmake
  • k8s故障处理经典案例(Classic Case of k8s Fault Handling)
  • 最新本地部署 DeepSeekR1 蒸馏\满血量化版 + WebOpenUI 完整教程(Ubuntu\Linux系统\Ollama)
  • 用户中心项目教程(九)---前端页面设计测试登录功能