Go语言并发编程 sync.Once

sync.Once用于保证某个动作只被执行一次,可用于单例模式中,比如初始化配置。我们知道init()函数也只会执行一次,不过它是在main()函数之前执行,如果想要在代码执行过程中只运行某个动作一次,可以使用sync.Once,下面来介绍一下它的使用方法。

先来看下面的代码:

package main

import (
 "fmt"
 "sync"
)

func main() {
 var num = 6
 var once sync.Once

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}

执行结果:

The num: 7
The num: 7

sync.Once类型提供了一个Do方法,Do方法只接受一个参数,且参数类型必须是func() ,也就是没有参数声明和结果声明的函数。

Do方法只会执行首次被调用时传入的那个函数,只执行一次,也不会执行其它函数。上面的例子中,即使传入的函数不同,也只会执行第一次传入的那个函数。如果有多个只执行一次的函数,需要为每一个函数分配一个sync.Once类型的值:

func main() {
 var num = 6
 var once1 sync.Once
 var once2 sync.Once

 add_one := func() {
  num = num + 1
 }

 minus_one := func() {
  num = num - 1
 } 

 once1.Do(add_one)
 fmt.Printf("The num: %d\n", num)
 once2.Do(minus_one)
 fmt.Printf("The num: %d\n", num)
}

sync.Once类型是一个结构体类型,一个是名为doneuint32类型字段,还有一个互斥锁m

type Once struct {
 done uint32
 m    Mutex
}

done字段的值只可能是0或者1,Do方法首次调用完成后,done的值就变为了1。done的值使用四个字节的uint32类型的原因是为了保证对它的操作是“原子操作”,通过调用atomic.LoadUint32函数获取它的值,如果为1,直接返回,不会执行函数。

如果为0,Do方法会立即锁定字段m,如果这里不加锁,多个goroutine 同时执行到Do方法时判断都为0,则都会执行函数,所以Once是并发安全的。

加锁之后,会再次检查done字段的值,如果满足条件,执行传入的函数,并用原子操作函数atomic.StoreUint32done的值设置为1。

下面是Once的源码:

func (o *Once) Do(f func()) {

 if atomic.LoadUint32(&o.done) == 0 {
  o.doSlow(f)
 }
}

func (o *Once) doSlow(f func()) {
 o.m.Lock()
 defer o.m.Unlock()
 if o.done == 0 {
  defer atomic.StoreUint32(&o.done, 1)
  f()
 }
}

源码非常简洁,和GoF 设计模式中的单例模式非常相似。

到此这篇关于Go语言并发编程 sync.Once的文章就介绍到这了,更多相关Go语言 sync.Once内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go语言并发编程之互斥锁Mutex和读写锁RWMutex

    目录 一.互斥锁Mutex 1.Mutex介绍 2.Mutex使用实例 二.读写锁RWMutex 1.RWMutex介绍 2.RWMutex使用实例 在并发编程中,多个Goroutine访问同一块内存资源时可能会出现竞态条件,我们需要在临界区中使用适当的同步操作来以避免竞态条件.Go 语言中提供了很多同步工具,本文将介绍互斥锁Mutex和读写锁RWMutex的使用方法. 一.互斥锁Mutex 1.Mutex介绍 Go 语言的同步工具主要由 sync 包提供,互斥锁 (Mutex) 与读写锁 (R

  • GO语言并发编程之互斥锁、读写锁详解

    在本节,我们对Go语言所提供的与锁有关的API进行说明.这包括了互斥锁和读写锁.我们在第6章描述过互斥锁,但却没有提到过读写锁.这两种锁对于传统的并发程序来说都是非常常用和重要的. 一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开方法--Lock和Unlock.顾名思义,前者被用于锁定当前的互斥量,而后者则被用来对当前的互斥量进行解锁. 类型sy

  • Go语言并发编程 sync.Once

    sync.Once用于保证某个动作只被执行一次,可用于单例模式中,比如初始化配置.我们知道init()函数也只会执行一次,不过它是在main()函数之前执行,如果想要在代码执行过程中只运行某个动作一次,可以使用sync.Once,下面来介绍一下它的使用方法. 先来看下面的代码: package main import ( "fmt" "sync" ) func main() { var num = 6 var once sync.Once add_one := fu

  • Go语言并发编程 互斥锁详情

    目录 1.互斥锁Mutex 1.1 Mutex介绍 1.2 Mutex使用实例 2.读写锁RWMutex 2.1 RWMutex介绍 2.2 RWMutex使用实例 1.互斥锁Mutex 1.1 Mutex介绍 Go 语言的同步工具主要由 sync 包提供,互斥锁 (Mutex) 与读写锁 (RWMutex) 就是sync 包中的方法. 互斥锁可以用来保护一个临界区,保证同一时刻只有一个 goroutine 处于该临界区内.主要包括锁定(Lock方法)和解锁(Unlock方法)两个操作,首先对进

  • 一文掌握Go语言并发编程必备的Mutex互斥锁

    目录 1. Mutex 互斥锁的基本概念 2. Mutex 互斥锁的基本用法 3. Mutex 互斥锁的底层实现 3.1 等待队列 3.2 锁状态 4. Mutex 互斥锁的注意事项 4.1 不要将 Mutex 作为函数或方法的参数传递 4.2 不要在获取 Mutex 的锁时阻塞太久 4.3 不要重复释放 Mutex 的锁 4.4 不要在锁内部执行阻塞或耗时操作 5. 总结 在并发编程中,我们需要处理多个线程同时对共享资源的访问问题.如果不加控制地同时访问共享资源,就会导致竞争条件(Race C

  • Go并发编程sync.Cond的具体使用

    目录 简介 详细介绍 案例:Redis连接池 注意点 简介 Go 标准库提供 Cond 原语的目的是,为等待 / 通知场景下的并发问题提供支持.Cond 通常应用于等待某个条件的一组 goroutine,等条件变为 true 的时候,其中一个 goroutine 或者所有的 goroutine 都会被唤醒执行. Cond 是和某个条件相关,这个条件需要一组 goroutine 协作共同完成,在条件还没有满足的时候,所有等待这个条件的 goroutine 都会被阻塞住,只有这一组 goroutin

  • Go语言并发编程基础上下文概念详解

    目录 前言 1 Go 中的 Context 2 Context 接口 3 Context Tree 4 创建上下文 4.1 上下文创建函数 4.2 Context 使用规范 4.3 Context 使用场景 5 总结 前言 相信大家以前在做阅读理解的时候,一定有从老师那里学一个技巧或者从参考答案看个:结合上下文.根据上下文我们能够找到有助于解题的相关信息,也能更加了解段落的思想. 在开发过程中,也有这个上下文(Context)的概念,而且上下文也必不可少,缺少上下文,就不能获取完整的程序信息.那

  • go并发编程sync.Cond使用场景及实现原理

    目录 使用场景 实现原理 copyChecker Wait Signal Broadcast 使用场景 sync.Cond是go标准库提供的一个条件变量,用于控制一组goroutine在满足特定条件下被唤醒. sync.Cond常用于一组goroutine等待,一个goroutine通知(事件发生)的场景.如果只有一个goroutine等待,一个goroutine通知(事件发生),使用Mutex或者Channel就可以实现. 可以用一个全局变量标志特定条件condition,每个sync.Con

  • C语言通过案例讲解并发编程模型

    目录 1.按照指定的顺序输出 2.生产者消费者模型 3.读写锁 下面代码.思路等来源于b站郭郭 和CSAPP样例,同时希望大家好好读一下CSAPP的内容,真的讲的很好 1.按照指定的顺序输出 我们执行两个线程:foo1 和foo2 foo1:打印step1, step3 foo2:打印step2 请用并发使得按照1 2 3 的顺序输出 答:首先两个线程执行顺序不可预判,我们必须保证打印step2之前step1就打印好了,因此需要阻塞一下step2,实现的方式是初始化sem为0,只有打印完step

  • Go并发编程中sync/errGroup的使用

    目录 一.序 二.errGroup 2.1 函数签名 三.源码 3.1 Group 3.2 WaitContext 3.3 Go 3.4 Wait 四. 案例 五. 参考 一.序 这一篇算是并发编程的一个补充,起因是当前有个项目,大概の 需求是,根据kafka的分区(partition)数,创建同等数量的 消费者( goroutine)从不同的分区中消费者消费数据,但是总有某种原因导致,某一个分区消费者创建失败,但是其他分区消费者创建失败. 最初的逻辑是,忽略分区失败的逻辑,将成功创建的分区消费

随机推荐