Golang标准库sync的使用

简介: Golang标准库sync的使用

o语言作为现代编程语言,其并发编程的优势是有目共睹的。在实际编程中,我们常常需要保证多个goroutine之间的同步,这就需要使用到Go语言的sync标准库sync库提供了基本的同步原语,例如互斥锁(Mutex)和等待组(WaitGroup),这些都是协调和控制并发执行的重要工具。

基础应用

1. 使用Mutex实现互斥

在很多情况下,我们需要保证在任意时刻只有一个goroutine能够访问某个数据。这时我们就可以使用Mutex(互斥锁)来实现这个需求。Mutex有两个方法:LockUnlock。在LockUnlock之间的代码块,同一时刻只有一个goroutine可以执行,其他尝试执行这部分代码的goroutine会被阻塞直到锁被解开。以下面的例子为例,我们试图在多个goroutine中对一个全局变量完成加法操作:

package main

import (
    "fmt"
    "sync"
)

var (
    sum int
    mu  sync.Mutex
)

func worker() {
    for i := 0; i < 10; i++ {
        mu.Lock()
        sum = sum + 1
        mu.Unlock()
    }
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            worker()
        }()
    }

    wg.Wait()
    fmt.Println(sum)
}

2. 使用WaitGroup等待并发操作结束

在另外一种常见的应用场景中,我们需要开启一组goroutine去处理任务,而主goroutine需要等待这些任务完成后才能结束。这可以通过sync.WaitGroup来实现。WaitGroup有三个方法:Add增加计数,Done减少计数,Wait等待计数归零。

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("Worker %d starting\n", id)

    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
}

以上的代码中,我们创建了5个worker goroutine,main goroutine 会等待所有的worker都完成工作后才会退出。

进阶应用

以下是一些稍微复杂一些的sync库的使用例子。

全局单例

使用sync.Once来保证某个操作只执行一次。这在初始化全局变量或者单例模式中非常有用。

 package main

 import (
    "fmt"
    "sync"
 )

 var once sync.Once

 func setup() {
     fmt.Println("Init function")
 }

 func worker(wg *sync.WaitGroup, n int) {
     once.Do(setup)
     fmt.Println("Worker", n)
     wg.Done()
 }

 func main() {
     var wg sync.WaitGroup

     for i := 0; i < 10; i++ {
         wg.Add(1)
         go worker(&wg, i)
     }

     wg.Wait()
 }

在以上代码中,我们保证了setup函数只被执行了一次,即使有10个goroutine都尝试去执行setup函数。

条件变量

使用sync.Cond来实现条件变量,这可以使得一个或者多个goroutine等待或者通知事件。

 package main

 import (
     "fmt"
     "sync"
     "time"
 )

 // Button 定义一个按钮结构体
 type Button struct {
     Clicked *sync.Cond
 }

 // 模拟一个用户界面
 func simulate(button *Button) {
     time.Sleep(time.Second)

     // 用户点击button
     button.Clicked.Broadcast()
 }

 func main() {
     // 初始化Button
     button := Button{Clicked: sync.NewCond(&sync.Mutex{})}

     // 显示信息的goroutine
     for i := 0; i < 10; i++ {
         go func(i int) {
             // 等待button被点击
             button.Clicked.L.Lock()
             defer button.Clicked.L.Unlock()
             button.Clicked.Wait()
 
             // button被点击,显示一条信息
             fmt.Println("button clicked,", i)
         }(i)
     }

     // 模拟用户点击button
     go simulate(&button)

     time.Sleep(2 * time.Second)
 }

处理并发是使用Go语言开发中非常重要的一部分,理解和掌握sync标准库中的同步原语对于你写出高效可靠的并发程序是非常重要的。


希望这篇文章能够帮助你更好的理解如何在Go语言中进行并发编程,我会非常高兴收到你的反馈~

相关文章
|
27天前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
36 4
|
1月前
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
|
3月前
|
Unix Go
Golang语言标准库time之日期和时间相关函数
这篇文章是关于Go语言日期和时间处理的文章,介绍了如何使用Go标准库中的time包来处理日期和时间。
64 3
|
4月前
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
340 2
|
4月前
|
存储 JSON Go
一文搞懂 Golang 高性能日志库 Zerolog
一文搞懂 Golang 高性能日志库 Zerolog
463 0
|
4月前
|
JSON Go 数据格式
[golang]标准库-json
[golang]标准库-json
|
6月前
|
SQL NoSQL Go
技术经验分享:Golang标准库:errors包应用
技术经验分享:Golang标准库:errors包应用
46 0
|
7月前
|
Shell Go API
7天玩转 Golang 标准库之 os
7天玩转 Golang 标准库之 os
60 1
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
140 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
71 4
Golang语言文件操作快速入门篇