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

这篇具有很好参考价值的文章主要介绍了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可以直接返回该结果。文章来源地址https://www.toymoban.com/news/detail-810588.html

到了这里,关于Go语言中的同步原语:ErrGroup、Semaphore和SingleFlight的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Go语言】Go语言中的变量和常量

    Go语言中的变量和常量 1 变量 变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。 Go 语言是强类型静态语言,所以变量的声明与赋值方式与 PHP/Python 等动态语言相比有很大的区别。

    2024年02月21日
    浏览(47)
  • 【go语言基础】go中的方法

    先思考一个问题,什么是方法,什么是函数? 方法是从属于某个结构体或者非结构体的。在func这个和方法名中间加了一个特殊的接收器类型,这个接收器可以是结构体类型的或者是非结构体类型的。从属的结构体获取该方法。 函数则没有这种从属关系。 小结: 大多

    2024年02月13日
    浏览(31)
  • Go 语言中的锁

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

    2024年01月16日
    浏览(42)
  • go语言中的切片

    切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 切片是一个引用类型,它的内部结构包含 地址 、 长度 和 容量 。切片一般用于快速地操作一块数据集合。 切片的本质就是对底层数组的封装,它包含了

    2024年02月11日
    浏览(41)
  • Go语言中的Pool

    Go语言中的pool是一个资源池,它可以存储一定数量的资源,这些资源可以被多个goroutine共享。Pool可以提高资源的利用率,减少资源的创建和销毁带来的开销。 Pool的实现原理很简单,它使用一个队列来存储资源。当一个goroutine需要使用资源时,它可以从队列中获取一个资源。

    2024年01月17日
    浏览(38)
  • go 语言中的 iota

    我们经常会在我们的代码中定义类似以下这些常量: 在其他时候,我们仅仅关注能把一个东西与其他的做区分。 有些时候,有些时候一件事没有本质上的意义。比如,我们在一个数据库表中存储产品,我们可能不想以 string 存储他们的分类。我们不关注这个分类是怎样命名的

    2024年01月23日
    浏览(36)
  • Go语言中的Channel

    Channel是Go语言中一种重要的并发原语,它允许goroutine之间安全地交换数据。Channel是一个类型化的队列,它可以存储一个特定类型的值。goroutine可以通过发送和接收操作来向channel中写入和读取数据。 Channel的类型由其元素类型和容量决定。元素类型是指channel中存储的值的类型

    2024年01月17日
    浏览(32)
  • Go语言中的原子操作

    在并发编程中,多个协程同时访问和修改共享数据时,如果没有使用适当的机制来防止并发问题,这个时候可能导致不确定的结果、数据不一致性、逻辑错误等严重后果。 而原子操作是解决并发编程中共享数据访问问题的一种常见机制。因此接下来的文章内容将深入介绍原子

    2024年02月09日
    浏览(46)
  • 【Go 基础篇】深入探索:Go语言中的二维数组

    在计算机编程中,数组是一种基本的数据结构,用于存储相同类型的元素。而二维数组作为数组的一种扩展,允许我们以类似表格的方式存储和处理数据。在Go语言中,二维数组是一个重要的概念,本文将深入探讨Go语言中的二维数组,包括定义、初始化、遍历以及应用场景等

    2024年02月10日
    浏览(40)
  • Go语言中的结构体详解

    Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似。和其他面向对 象语言中的类相比,Golang 中的结构体具有更高的扩展性和灵活性。 Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全 部或部分属性时,这时候再用单

    2024年02月15日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包