Go语言中的sync包同步原语最新详解

 更新时间:2023年12月27日 11:55:31   作者:技术的游戏  
Go语言在sync包中提供了一套多才多艺的同步机制,以及用于管理对共享资源的并发访问的原子操作,了解这些工具并为您的并发需求选择合适的工具是编写高效可靠的并发Go程序的关键,这篇文章主要介绍了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 main
import (
	"fmt"
	"sync"
	"time"
)
func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done() // Decrement the Wait Group counter when done
	fmt.Printf("Worker %d is working\n", id)
	time.Sleep(time.Second)
	fmt.Printf("Worker %d has finished\n", id)
}
func main() {
	var wg sync.WaitGroup
	for i := 1; i <= 3; i++ {
		wg.Add(1) // Increment the Wait Group counter for each Goroutine
		go worker(i, &wg)
	}
	wg.Wait() // Wait for all Goroutines to finish
	fmt.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 main
import (
	"fmt"
	"sync"
	"time"
)
var (
	data        int
	dataMutex   sync.RWMutex
)
func readData() int {
	dataMutex.RLock() // Read Lock
	defer dataMutex.RUnlock()
	return data
}
func writeData(value int) {
	dataMutex.Lock() // Write Lock
	defer dataMutex.Unlock()
	data = value
}
func main() {
	// Read data concurrently
	for i := 1; i <= 5; i++ {
		go func() {
			fmt.Println("Read Data:", readData())
		}()
	}
	// Write data
	writeData(42)
	time.Sleep(time.Second)
}

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

Cond(条件变量)

什么是条件变量?

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

如何使用Cond

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

package main
import (
	"fmt"
	"sync"
	"time"
)
var (
	conditionMutex sync.Mutex
	condition      *sync.Cond
	isReady        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 = true
	condition.Signal() // Signal one waiting Goroutine
	conditionMutex.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 main
import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)
var (
	counter int32
	wg      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函数确保递增操作是原子的,并且对并发访问是安全的。

选择正确的同步机制

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

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

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

到此这篇关于Go语言中的`sync`包同步原语的文章就介绍到这了,更多相关go sync同步原语内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • gin自定义中间件解决requestBody不可重复读问题(最新推荐)

    gin自定义中间件解决requestBody不可重复读问题(最新推荐)

    这篇文章主要介绍了gin自定义中间件解决requestBody不可重复读问题,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • golang语言如何将interface转为int, string,slice,struct等类型

    golang语言如何将interface转为int, string,slice,struct等类型

    这篇文章主要介绍了golang语言如何将interface转为int, string,slice,struct等类型,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Golang websocket协议使用浅析

    Golang websocket协议使用浅析

    这篇文章主要介绍了Golang websocket协议的使用,WebSocket是一种新型的网络通信协议,可以在Web应用程序中实现双向通信,感兴趣想要详细了解可以参考下文
    2023-05-05
  • pytorch中的transforms.ToTensor和transforms.Normalize的实现

    pytorch中的transforms.ToTensor和transforms.Normalize的实现

    本文主要介绍了pytorch中的transforms.ToTensor和transforms.Normalize的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Go语言按字节截取字符串的方法

    Go语言按字节截取字符串的方法

    这篇文章主要介绍了Go语言按字节截取字符串的方法,涉及Go语言操作字符串的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-02-02
  • Golang实现http server提供压缩文件下载功能

    Golang实现http server提供压缩文件下载功能

    这篇文章主要介绍了Golang实现http server提供压缩文件下载功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • go goquery网页解析实现示例

    go goquery网页解析实现示例

    这篇文章主要为大家介绍了go goquery网页解析实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Go语言实现Sm2加解密的示例代码

    Go语言实现Sm2加解密的示例代码

    本文主要介绍了Go语言实现Sm2加解密的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • go-zero源码阅读之布隆过滤器实现代码

    go-zero源码阅读之布隆过滤器实现代码

    布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难,这篇文章主要介绍了go-zero源码阅读-布隆过滤器,需要的朋友可以参考下
    2023-02-02
  • golang操作elasticsearch的实现

    golang操作elasticsearch的实现

    这篇文章主要介绍了golang操作elasticsearch,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06

最新评论