Go语言中的同步原语:ErrGroup、Semaphore和SingleFlight

1. 并发基础

并发是同时发生多个计算或事件的能力。并发通常通过同时执行多个任务或进程来实现,这些任务或进程共享相同的资源(例如内存或处理器)。并发使用的基本机制被称为锁。在Go语言中,锁是一个类型变量,它包含一个内部计数器,用于跟踪已获取的锁的数量。当一个goroutine获取一个锁时,它会将计数器增加一;当一个goroutine释放一个锁时,它会将计数器减少一。

2. 同步原语

同步原语是一组特殊的变量或类型,用于在并发程序中协调goroutine之间的通信和同步。Go语言中提供了丰富的同步原语,包括互斥锁(mutex)、读写锁(RWMutex)、等待组(WaitGroup)、一次性锁(Once)、条件变量(Cond)、错误组(ErrGroup)、信号量(Semaphore)和单次调用(SingleFlight)。

3. ErrGroup

ErrGroup是一个同步原语,它允许一组goroutine并发地执行任务,并收集所有goroutine执行过程中发生的错误。ErrGroup包含一个内部错误列表,当任何一个goroutine在执行任务时发生错误,该错误将被添加到错误列表中。ErrGroup还提供了一个Wait方法,该方法将阻塞当前goroutine,直到所有goroutine都完成执行任务,或者发生错误。

package main

import (
    "context"
    "fmt"
    "sync"
)

func main() {
    // 创建一个错误组
    var eg sync.ErrGroup

    // 创建三个goroutine来并发地执行任务
    for i := 0; i < 3; i++ {
        i := i
        eg.Go(func() error {
            // 模拟任务执行
            if i == 2 {
                return fmt.Errorf("error occurred in goroutine %d", i)
            }
            return nil
        })
    }

    // 等待所有goroutine完成执行任务
    if err := eg.Wait(); err != nil {
        fmt.Println(err) // 输出:error occurred in goroutine 2
    }
}

在这个示例中,我们使用ErrGroup来收集三个goroutine执行过程中发生的错误。如果任何一个goroutine在执行任务时发生错误,该错误将被添加到错误列表中,并最终在Wait方法中被打印出来。

4. Semaphore

Semaphore是一个同步原语,它用于限制可以同时访问共享资源的goroutine数量。Semaphore包含一个内部计数器,该计数器表示可用的资源数量。当一个goroutine需要访问共享资源时,它必须先获取Semaphore,如果Semaphore的计数器大于0,则该goroutine可以访问共享资源,否则该goroutine将被阻塞,直到Semaphore的计数器大于0。当一个goroutine不再需要访问共享资源时,它必须释放Semaphore,以允许其他goroutine访问共享资源。

package main

import (
    "context"
    "fmt"
    "sync"
)

func main() {
    // 创建一个信号量,限制同时可以访问共享资源的goroutine数量为2
    sem := make(chan struct{}, 2)

    // 创建三个goroutine来并发地访问共享资源
    for i := 0; i < 3; i++ {
        i := i
        go func() {
            // 获取信号量
            sem <- struct{}{}

            // 模拟访问共享资源
            fmt.Println("Goroutine", i, "is accessing the shared resource.")
            time.Sleep(1 * time.Second)

            // 释放信号量
            <-sem
        }()
    }

    // 等待所有goroutine完成
    time.Sleep(3 * time.Second)
}

在这个示例中,我们使用Semaphore来限制同时可以访问共享资源的goroutine数量为2。当一个goroutine需要访问共享资源时,它必须先获取Semaphore,如果Semaphore的计数器大于0,则该goroutine可以访问共享资源,否则该goroutine将被阻塞,直到Semaphore的计数器大于0。

5. SingleFlight

SingleFlight是一个同步原语,它确保某个操作只会被执行一次。SingleFlight包含一个内部映射,该映射将操作的key映射到操作的结果。当一个goroutine需要执行某个操作时,它必须先检查SingleFlight的内部映射中是否已经存在该操作的结果。如果存在,则该goroutine直接返回该结果,否则该goroutine将执行该操作,并将结果存储在SingleFlight的内部映射中,以便其他goroutine可以直接返回该结果。

package main

import (
    "context"
    "fmt"
    "sync"
)

var sf sync.SingleFlight

func main() {
    // 定义一个需要执行的操作
    fn := func() (int, error) {
        // 模拟执行操作
        return 100, nil
    }

    // 并发地执行该操作10次
    for i := 0; i < 10; i++ {
        go func() {
            // 获取操作的结果
            result, err := sf.Do("key", fn)
            if err != nil {
                fmt.Println(err)
                return
            }

            fmt.Println("Result:", result)
        }()
    }

    // 等待所有goroutine完成
    time.Sleep(1 * time.Second)
}

在这个示例中,我们使用SingleFlight来确保fn函数只会被执行一次。当一个goroutine需要执行fn函数时,它必须先检查SingleFlight的内部映射中是否已经存在fn函数的结果。如果存在,则该goroutine直接返回该结果,否则该goroutine将执行fn函数,并将结果存储在SingleFlight的内部映射中,以便其他goroutine可以直接返回该结果。

相关推荐

  1. Go语言同步原语:ErrGroup、SemaphoreSingleFlight

    2024-01-13 19:20:01       71 阅读
  2. gosingleflight

    2024-01-13 19:20:01       39 阅读
  3. gosingleflight学习

    2024-01-13 19:20:01       48 阅读
  4. Go语言,常用同步机制

    2024-01-13 19:20:01       34 阅读
  5. Go】探索Go语言panicrecover

    2024-01-13 19:20:01       43 阅读
  6. Go语言如何实现协程同步

    2024-01-13 19:20:01       30 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-13 19:20:01       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-13 19:20:01       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-13 19:20:01       87 阅读
  4. Python语言-面向对象

    2024-01-13 19:20:01       96 阅读

热门阅读

  1. 网络设备远程运维管理解决方案

    2024-01-13 19:20:01       65 阅读
  2. ReactHooks:useEffect使用指南

    2024-01-13 19:20:01       76 阅读
  3. Vue的响应式编程

    2024-01-13 19:20:01       63 阅读
  4. Driver.js使用指南

    2024-01-13 19:20:01       65 阅读
  5. TypeScript基础知识:类型守卫和类型推断

    2024-01-13 19:20:01       65 阅读
  6. 【WPF.NET开发】WPF中的拖放

    2024-01-13 19:20:01       58 阅读
  7. C# Chart控件

    2024-01-13 19:20:01       49 阅读