Go语言中的`sync`包同步原语

2023-12-25 07:28
文章标签 语言 go 同步 sync 原语

本文主要是介绍Go语言中的`sync`包同步原语,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

通过sync包掌握Go语言的并发

并发是现代软件开发的基本方面,而Go(也称为Golang)为并发编程提供了一套强大的工具。在Go中用于管理并发的基本包之一是sync包。在本文中,我们将概述sync包,并深入探讨其最关键的同步原语之一:等待组(Wait Groups)。

sync包概述

sync包是Go的标准库包,为并发编程提供了同步原语。它为开发人员提供了协调和同步Goroutines的工具,确保并发任务的安全和有序执行。sync包提供的一些关键同步原语包括Mutexes、RWMutexes、Cond和Wait Groups。

等待组(Wait Groups)

什么是等待组?

等待组是Go中sync包提供的一个同步原语。它是一个简单但强大的工具,用于管理Goroutines的同步,特别是当您希望在继续之前等待一组Goroutines完成其任务时。

等待组在您有多个Goroutines同时执行独立任务,并且您需要确保所有任务都已完成后再继续主程序的场景中非常有用。

如何使用等待组

让我们通过一个代码示例来探索如何使用等待组:

package mainimport ("fmt""sync""time"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done() // Decrement the Wait Group counter when donefmt.Printf("Worker %d is working\n", id)time.Sleep(time.Second)fmt.Printf("Worker %d has finished\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1) // Increment the Wait Group counter for each Goroutinego worker(i, &wg)}wg.Wait() // Wait for all Goroutines to finishfmt.Println("All workers have finished.")
}

在这个示例中,我们定义了一个名为worker的函数,该函数通过休眠一秒来模拟工作。我们启动了三个Goroutines,每个代表一个工作者,并使用sync.WaitGroup来协调它们的执行。

  • wg.Add(1) 在启动每个Goroutine之前增加等待组计数器。
  • wg.Done()worker函数中被延迟执行,以在Goroutine完成其工作时减少计数器。
  • wg.Wait() 阻塞主程序,直到所有Goroutines都完成,确保我们等待所有工作者的完成。

RWMutex(读写互斥锁)

RWMutex(读写互斥锁)是Go语言中的一个同步原语,它允许多个Goroutines同时读取共享数据,同时确保写入时的独占访问。在数据频繁读取但较少修改的场景中,它非常有用。

如何使用RWMutex

以下是一个简单的示例,演示如何使用RWMutex:

package mainimport ("fmt""sync""time"
)var (data        intdataMutex   sync.RWMutex
)func readData() int {dataMutex.RLock() // Read Lockdefer dataMutex.RUnlock()return data
}func writeData(value int) {dataMutex.Lock() // Write Lockdefer dataMutex.Unlock()data = value
}func main() {// Read data concurrentlyfor i := 1; i <= 5; i++ {go func() {fmt.Println("Read Data:", readData())}()}// Write datawriteData(42)time.Sleep(time.Second)
}

在这个示例中,多个Goroutines同时读取共享的data,而一个单独的Goroutine则对其进行写入。RWMutex确保多个读取者可以同时访问数据,但只有一个写入者可以在任何时候修改它。

Cond(条件变量)

什么是条件变量?

条件变量是一种同步原语,允许Goroutines在继续执行之前等待特定条件变为真。当您需要基于某些条件协调多个Goroutines的执行时,它们非常有用。

如何使用Cond

以下是一个基本示例,说明了如何使用条件变量:

package mainimport ("fmt""sync""time"
)var (conditionMutex sync.Mutexcondition      *sync.CondisReady        bool
)func waitForCondition() {conditionMutex.Lock()defer conditionMutex.Unlock()for !isReady {fmt.Println("Waiting for the condition...")condition.Wait()}fmt.Println("Condition met, proceeding.")
}func setCondition() {time.Sleep(2 * time.Second)conditionMutex.Lock()isReady = truecondition.Signal() // Signal one waiting GoroutineconditionMutex.Unlock()
}func main() {condition = sync.NewCond(&conditionMutex)go waitForCondition()go setCondition()time.Sleep(5 * time.Second)
}

在这个示例中,一个Goroutine使用condition.Wait()等待条件变为真,而另一个Goroutine将条件设置为true并使用condition.Signal()通知等待的Goroutine。

原子操作

什么是原子操作?

原子操作是作为单个、不可分割的工作单元执行的操作。它们通常用于在并发程序中安全地更新共享变量,而无需使用互斥锁。Go提供了一个名为atomic的包来进行原子操作。

如何使用原子操作

以下是一个演示原子操作的示例:

package mainimport ("fmt""sync""sync/atomic""time"
)var (counter int32wg      sync.WaitGroup
)func incrementCounter() {defer wg.Done()for i := 0; i < 100000; i++ {atomic.AddInt32(&counter, 1)}
}func main() {wg.Add(2)go incrementCounter()go incrementCounter()wg.Wait()fmt.Println("Counter:", atomic.LoadInt32(&counter))
}

在这个示例中,两个Goroutines使用原子操作递增一个共享的counter变量。atomic.AddInt32函数确保递增操作是原子的,并且对并发访问是安全的。

选择正确的同步机制

在选择适当的同步机制时,请考虑以下准则:

  1. 互斥锁(对于读取使用RWMutex,对于写入使用Mutex) 在你需要对访问进行细粒度控制时,非常适合保护共享数据。
  2. 条件变量 在你需要基于特定条件协调Goroutines时非常有价值。
  3. 原子操作 在你想避免互斥锁开销的情况下,对共享变量进行简单操作非常高效。
  4. 始终选择最能满足特定用例要求的同步机制。

总之,Go语言在sync包中提供了一套多才多艺的同步机制,以及用于管理对共享资源的并发访问的原子操作。了解这些工具并为您的并发需求选择合适的工具是编写高效可靠的并发Go程序的关键。

这篇关于Go语言中的`sync`包同步原语的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/534674

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

go基础知识归纳总结

无缓冲的 channel 和有缓冲的 channel 的区别? 在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。 无缓冲的 channel 行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

C语言:柔性数组

数组定义 柔性数组 err int arr[0] = {0}; // ERROR 柔性数组 // 常见struct Test{int len;char arr[1024];} // 柔性数组struct Test{int len;char arr[0];}struct Test *t;t = malloc(sizeof(Test) + 11);strcpy(t->arr,