078-只被执行一次的函数
Posted --Allen--
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了078-只被执行一次的函数相关的知识,希望对你有一定的参考价值。
倘若你看过我的所有文章的话,你肯定还记得曾经在学习《Linux 环境编程》的时候,也有一篇类似的文章,标题和这个一模一样——《只被执行一次的函数》。
我强烈建议你回顾一下上面那篇文章。如果你没有《Linux 环境编程》的基础,强烈建议你补习一下,只会 Golang 是不行的。
1. 背景
相信很多同学都使用过单例模式。如果在单线程程序中,单例模式肯定没啥问题,但是如果在多线程程序中,可能就容易出问题了。当然,很多同学想到的方法是加锁来解决,像下面这样:
T* getInstance()
mutex.lock()
if (_instance == nullptr)
_instance = new T();
mutex.unlock()
return _instance;
这个加锁粒度太大了,而且每次访问都需要加锁。也有同学会说,使用 static 全局初始化不就好了(有人叫它为饿汉模式)。没错,这是一种可行的方法,但是缺点是有时候可能一次也用不到这个单例,一上来就初始化有点太浪费。
那使用 double-check 方法改一下(所谓的线各安全的懒汉模式)?
T* getInstance()
if (_instance == nullptr)
mutex.lock()
if (_instance == nullptr)
_instance = new T();
mutex.unlock()
return _instance;
很不幸运的是,即使是 double-check,上面的代码在多线程环境中还是会出问题。比如线程 1 执行 _instance = new T()
,_instance 已经不为 nullptr 了,但是实际上 T 对象还没有构造出来,即构造函数还未执行。(对象构造必须是先申请内存,再执行构造函数)。这个时候线程 2 在第一个 if 条件看到的 _instance 不为 nullptr,于是返回……
看起来好像挺麻烦的。不用怕,我们有杀器,可以更加简单且完美的解决这个问题——那就是 sync.Once 类型。
2. 简单的 demo
为了让你快速体验 sync.Once 的作用,来看下面一段代码。
package main
import (
"fmt"
"sync"
)
func hello()
fmt.Println("only once")
func main()
var once sync.Once
once.Do(hello) // 把 hello 函数托管给 once 对象去执行。
fmt.Println("first")
once.Do(hello) // 第二次托管,once 就不理我们了。。。
fmt.Println("second")
// Output:
// only once
// first
// second
其实,once.Do 也是“协程”安全的,你甚至可以这样:
func main()
var once sync.Once
var wg sync.WaitGroup
wg.Add(10)
// 10 个协程同时托管 hello 函数给 once
for i := 0; i < 10; i++
go func()
once.Do(hello)
wg.Done()
()
wg.Wait()
fmt.Println("first")
fmt.Println("second")
// Output:
// only once
// first
// second
3. 使用 once 来实现懒汉单例
package main
import (
"fmt"
"sync"
)
type Configure struct
volume int32
var once sync.Once
var configure *Configure
func initConfig()
configure = &Configure10
func GetInstance() *Configure
// initConfig()
once.Do(initConfig) // 注释这一行,打开上一行试试
return configure
func main()
var conf *Configure
fmt.Printf("%p\\n", conf)
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++
go func()
conf = GetInstance()
fmt.Printf("%p\\n", conf)
wg.Done()
()
wg.Wait()
4. 总结
- 掌握 sync.Once 的作用和使用方法
以上是关于078-只被执行一次的函数的主要内容,如果未能解决你的问题,请参考以下文章