GoLang中的sync包Once使用执行示例

目录
  • 背景
  • One简介
  • 示例
  • 注意
  • 源码解读

背景

在系统初始化的时候,某些代码只想被执行一次,这时应该怎么做呢,没有学习 Once 前,大家可能想到 声明一个标识,表示是否初始化过,然后初始化这个标识加锁,更新这个标识。

但是学会了 One 的使用可以更加简单的解决这个问题

One简介

Once 包主要用于在并发执行代码的时候,某部分代码只会被执行 一次。

Once 的使用也非常简单,Once 只有一个 Do 方法,接收一个无参数无返回值的函数类型的参数 f,不管调用多少次 Do 方法,参数 f 只在第一次调用 Do 方法时执行。

示例

我们有一个Msg 参数,多个协程都会用到他,但是这个参数只用初始化一次就可以。

package main
import (
	"fmt"
	"sync"
	"time"
)
var msg string
func main() {
	var one sync.Once
	for i := 0; i < 5; i++ {
		go func(i int) {
			one.Do(func() {
				fmt.Printf("%d 执行初始化!\n", i)
				msg = "Your Need Data"
			})
			fmt.Println(msg)
		}(i)
	}
	time.Sleep(3* time.Second)
}

执行结果如下:

可以看到初始化的代码只被4号线程执行了一次, 其他协程都是直接读的初始化的数据,并没有执行初始化的函数。

注意

不要在 Do() 方法的参数方法中再次调用Do() 方法,因为执行这个Do() 方法的参数方法的时候,One 会持有一个锁,如果再参数方法中再次调用Do() 方法,就会等待这个锁释放, 导致参数方法无法执行完毕,然后外层的Do 方法就一直无法释放锁,最后就成了死锁。

错误示例:

package main
import (
	"fmt"
	"sync"
)
var msg string
var one sync.Once
func main() {
	one.Do(fun1)
}
func fun1(){
	fmt.Println("我是 fun1")
	one.Do(fun2)
}
func fun2(){
	fmt.Println("我是 fun2")
}

执行结果:

可以知道再 fun1() 中使用 Do() 方法调用 fun2 的时候形成了死锁, 因为在 fun1() 执行过程中已将持有了该锁,需要 fun1() 执行完毕才会释放,然后因为使用 Do() 方法执行 fun2() 也会请求这个锁, 会一直等待,导致 fun1() 不可能执行完, 也不可能释放锁。成了死锁。

源码解读

查看源码

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {
		// Outlined slow-path to allow inlining of the fast-path.
		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()
	}
}

使用一个原子类作为标识,加锁校验和操作原子类,保证只会被一个协程执行。

Do 调用了 doSlow , 在 doSlow 中有defer 关键字,表示执行函数和释放锁是倒序执行,必须先执行完毕 if 判断和里面的 f() 才能释放锁。

到此这篇关于GoLang中的sync包Once使用执行示例的文章就介绍到这了,更多相关Go sync Once内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • GoLang的sync.WaitGroup与sync.Once简单使用讲解

    目录 一.sync.WaitGroup的简单实用 Add方法 Done方法 Wait方法 二.sync.WaitGroup类型值中计数器的值可以小于0吗 三.sync.Once 用法 sync.Once类型中的uint32类型的字段 Do方法的功能特点 一.sync.WaitGroup的简单实用 在之前,我们使用通道,来主goroutine中等待其他goroutine执行完成: func coordinateWithChan() { sign := make(chan struct{}, 2)

  • go sync Once实现原理示例解析

    目录 正文 Once 的实现 使用示例 Once 的一些工作机制 Once 详解 hotpath atomic.LoadUint32 atomic.StoreUint32 Mutex 总结 正文 在很多情况下,我们可能需要控制某一段代码只执行一次,比如做某些初始化操作,如初始化数据库连接等. 对于这种场景,go 为我们提供了 sync.Once 对象,它保证了某个动作只被执行一次. 当然我们也是可以自己通过 Mutex 实现 sync.Once 的功能,但是相比来说繁琐了那么一点, 因为我们不仅

  • 一文解析 Golang sync.Once 用法及原理

    目录 前言 1. 定位 2. 对外接口 3. 实战用法 3.1 初始化 3.2 单例模式 3.3 关闭channel 4. 原理 5. 避坑 前言 在此前一篇文章中我们了解了 Golang Mutex 原理解析,今天来看一个官方给出的 Mutex 应用场景:sync.Once. 1. 定位 Once is an object that will perform exactly one action. sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式,例如初始化配

  • Go并发编程之sync.Once使用实例详解

    目录 一.序 二. 源码分析 2.1结构体 2.2 接口 三. 使用场景案例 3.1 单例模式 3.2 加载配置文件示例 四.总结 五. 参考 一.序 单从库名大概就能猜出其作用.sync.Once使用起来很简单, 下面是一个简单的使用案例 package main import ( "fmt" "sync" ) func main() { var ( once sync.Once wg sync.WaitGroup ) for i := 0; i < 10;

  • Golang基于sync.Once实现单例的操作代码

    目录 基于sync.Once实现单例 单例类型定义Driver 类Field conn once.Do(func() {}) 并发访问once.Do() 对外暴露方法Conn() 重新new(Driver)会发生什么? 在go里实现单例模式有多种方式: 基于lock 基于init函数 基于sync.Once 本文介绍基于sync.Once的方式来实现单例,熟练掌握这种模式,并理解其底层原理,对大部分人来讲已经完全够用了. 基于sync.Once实现单例 // 其他package也可见,在其他地方

  • 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

  • GoLang中的sync包Once使用执行示例

    目录 背景 One简介 示例 注意 源码解读 背景 在系统初始化的时候,某些代码只想被执行一次,这时应该怎么做呢,没有学习 Once 前,大家可能想到 声明一个标识,表示是否初始化过,然后初始化这个标识加锁,更新这个标识. 但是学会了 One 的使用可以更加简单的解决这个问题 One简介 Once 包主要用于在并发执行代码的时候,某部分代码只会被执行 一次. Once 的使用也非常简单,Once 只有一个 Do 方法,接收一个无参数无返回值的函数类型的参数 f,不管调用多少次 Do 方法,参数

  • Golang中的sync包的WaitGroup操作

    sync的waitgroup功能 WaitGroup 使用多线程时,进行等待多线程执行完毕后,才可以结束函数,有两个选择 channel waitgroup 首先使用channel func add (n *int , isok chan bool){ for i :=0 ;i <1000 ; i ++ { *n = *n + 1 } isok <- true } func main () { var ok = make(chan bool , 2) var i,u = 0,0 go add(

  • 深入Golang中的sync.Pool详解

    我们通常用golang来构建高并发场景下的应用,但是由于golang内建的GC机制会影响应用的性能,为了减少GC,golang提供了对象重用的机制,也就是sync.Pool对象池. sync.Pool是可伸缩的,并发安全的.其大小仅受限于内存的大小,可以被看作是一个存放可重用对象的值的容器. 设计的目的是存放已经分配的但是暂时不用的对象,在需要用到的时候直接从pool中取. 任何存放区其中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩. sync.Pool首先

  • golang中使用sync.Map的方法

    背景 go中map数据结构不是线程安全的,即多个goroutine同时操作一个map,则会报错,因此go1.9之后诞生了sync.Map sync.Map思路来自java的ConcurrentHashMap 接口 sync.map就是1.9版本带的线程安全map,主要有如下几种方法: Load(key interface{}) (value interface{}, ok bool) //通过提供一个键key,查找对应的值value,如果不存在,则返回nil.ok的结果表示是否在map中找到值

  • 一文详解Golang中net/http包的实现原理

    目录 前言 http包执行流程 http包源码分析 端口监听 请求解析 路由分配 响应处理 前言 Go语言自带的net/http包提供了HTTP客户端和服务端的实现,实现一个简单的http服务非常容易,其自带了一些列结构和方法来帮助开发者简化HTTP服务开发的相关流程,因此我们不需要依赖任何第三方组件就能构建并启动一个高并发的HTTP服务器,net/http包在编写web应用中有很重要的作用,这篇文章会学习如何用 net/http 自己编写实现一个 HTTP Server 并探究其实现原理,具体

  • GO中sync包自由控制并发示例详解

    目录 资源竞争 sync.Mutex sync.RWMutex sync.WaitGroup sync.Once sync.Cond 资源竞争 channel 常用于并发通信,要保证并发安全,主要使用互斥锁.在并发的过程中,当一个内存被多个 goroutine 同时访问时,就会产生资源竞争的情况.这块内存也可以称为共享资源. 并发时对于共享资源必然会出现抢占资源的情况,如果是对某资源的统计,很可能就会导致结果错误.为保证只有一个协程拿到资源并操作它,可以引入互斥锁 sync.Mutex. syn

  • golang中的三个点 '...'的用法示例详解

    '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是slice可以被打散进行传递. 下面直接上例子: func test1(args ...string) { //可以接受任意个string参数 for _, v:= range args{ fmt.Println(v) } } func main(){ var strss= []string{ "qwr", "234", "yui

  • 在golang中使用Sync.WaitGroup解决等待的问题

    面对goroutine我们都需要等待它完成交给它的事情,等待它计算完成或是执行完毕,所以不得不在程序需要等待的地方使用time.Sleep()来睡眠一段时间,等待其他goroytine执行完毕,下面的代码打印1到100的for循环可以在很快的时间内运行完毕,但是我们必须添加time.Sleep()来等待其打印完毕,如果我们不等待仿佛什么也没有发生一样.....这肯定不是我们想要的! func main(){ for i := 0; i < 100 ; i++{ go fmt.Println(i)

  • 解决golang中container/list包中的坑

    golang中list包用法可以参看这篇文章 但是list包中大部分对于e *Element进行操作的元素都可能会导致程序崩溃,其根本原因是e是一个Element类型的指针,当然其也可能为nil,但是golang中list包中函数没有对其进行是否为nil的检查,变默认其非nil进行操作,所以这种情况下,便可能出现程序崩溃. 1.举个简单例子 Remove()函数 package main import ( "container/list" "fmt" ) func

  • Golang中的sync.WaitGroup用法实例

    WaitGroup的用途:它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成. 官方对它的说明如下: A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and

随机推荐