当前位置: 首页 > news >正文

上海网站建设制作百橙商家做网站的优点

上海网站建设制作百橙,商家做网站的优点,西安二手房,手机微网站建设案例及报告本文分析了Golang sync.Once 源码#xff0c;并由此引申#xff0c;简单讨论了单例模式的实现、 atomic 包的作用和 Java volatile 的使用。 sync.Once 使用例子 sync.Once 用于保证一个函数只被调用一次。它可以用于实现单例模式。 有如下类型#xff1a; type instanc…本文分析了Golang sync.Once 源码并由此引申简单讨论了单例模式的实现、 atomic 包的作用和 Java volatile 的使用。 sync.Once 使用例子 sync.Once 用于保证一个函数只被调用一次。它可以用于实现单例模式。 有如下类型 type instance struct {val int }假设我们需要单例模式且需要将 instance 的初始化延迟到第一次访问它的时候那么可以用 sync.Once只需将单例的初始化函数传给 Once.Do便可确保 initSingleton() 恰好执行一次。 var s *instance var once sync.Oncefunc initSingleton() {s new(instance)fmt.Println(instance is initializing...)time.Sleep(time.Second)s.val }func GetInstance() *instance {once.Do(initSingleton)return s }多个 goroutine 并发调用 GetInstance() 仍能保证 initSingleton() 恰好执行一次。 sync.Once 实现原理 sync.Once 内部非常简单只有一个标识传入的函数是否已经执行的无符号整型以及一个互斥锁。 type Once struct {done uint32m Mutex }由上述使用例子多个 goroutine 调用 Do 仍能保证传入的函数恰好被执行一次。 Do 首先检查其 done 成员是否为零若为零说明初始化还未完成这时加锁重新检查 done 的值确保还未初始化并调用初始化函数 f()。调用返回后将 done 修改为1指示已经初始化。 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()} }多个 goroutine 同时调用 Once.Do 会发生什么 假设多个 goroutine 发现 done 的值为零同时进入了 doSlow 方法因为 doSlow 方法需要加锁只有一个 goroutine 能够执行 f()其余 goroutine 将阻塞。当执行 f() 的 goroutine 返回前更新 done 值后解锁其余 goroutine 能够继续执行 doSlow再次检查 done发现已经不为零说明在等待锁的间隙已经有其它 goroutine 调用 f() 完成了初始化当前 goroutine 解锁并返回。 为什么加了锁之后不需要用原子读取函数 atomic.LoadUint32 这是因为互斥锁 m 保护了 done 字段不会被并发修改、读取。可以安全地读取 done。不同的是doSlow 之前对 done 的读取必须是原子读取否则这里将存在一个 data race。 为什么加锁后仍要用 atomic.StoreUint32而不是直接赋值 done 1 因为 done 不是 volatile 的直接赋值无法保证可见性。也不能确保 done 1 不被重排序到 f() 之前。关于 atomic load/store参考如下 What is the point of sync/atomic.(Load|Store)Int32 ? However, the atomic load and store provide another property. If one processor executes “a 1; b 1” (let’s say that a and b are always 0 before) and another processor executes “if b { c a }” then if the “b 1” uses a non-atomic store, or the “if b” uses a non-atomic load, then it is entirely possible that the second processor will read the old value of a and set c to 0. That is, using a non-atomic load or store does not provide any ordering guarantees with regard to other memory that may have been set by the other processor. You almost never care about only atomicity. There is also ordering (as Ian described) and visibility (loads/stores must be visible to other goroutines in a finite amount of time, this is not true for non-atomic loads/store). And there are also data races, which render behavior of your program undefined. All the same applies to C/C as well. Why supporting atomic.Load and atomic.Store in Go? Because of ordering guarantees, and memory operation visibility. For instance: y:0 x:0 x1 y1 In the above program, another goroutine can see (0,0), (0,1), (1,0), or (1,1) for x and y. This is because of compiler reordering the code, compiler optimization,s or because of memory operation reordering at the hardware level. However: y:0 x:0 x:1 atomic.StoreInt64(y,1) If another goroutine sees atomic.LoadInt64(y)1, then the goroutine is guaranteed to see x1. 为什么不能 atomic.CompareAndSwapUint32(o.done, 0, 1) 判断为 true 后直接调用 f() 初始化 如下所示 func (o *Once) Do(f func()) {if atomic.CompareAndSwapUint32(o.done, 0, 1) {f()} }多个 goroutine 进入 Do 时能够保证 f() 只被调用一次但是不能保证 goroutine 返回时初始化已经完成。但是这种方法可以用于 Once 的异步实现。即一个 goroutine 发现该实例还未初始化完成立刻返回并继续做其他事情。 单例的错误实现 sync.Once 利用 atomic 包实现了「只调用一次」的语义。可以只用一个互斥锁先判断是否初始化如果还没初始化加锁再判断是否已经初始化才进行初始化。如下 GetInstanceV2() 所示。 package singletonimport (sync )type instance struct {val int }var s *instance var once sync.Once var mu sync.Mutexfunc initSingleton() {s new(instance)fmt.Println(instance is initializing...)time.Sleep(time.Second)s.val }func GetInstance() *instance {once.Do(initSingleton)return s }func GetInstanceV2() *instance {// 先不加锁判断if s nil {// 未初始化加锁mu.Lock()defer mu.Unlock()// 加锁后重新判断if s nil {// 进行初始化initSingleton()}}return s }事实上在 GetInstanceV2 中第一次读取 s 没有加锁又因为 s 不是 volatile 类型的Go 也没有 volatile当能够看到 s ! nil 时也不能保证 s 已经初始化完成所以 GetInstanceV2 实现是有问题的。如果用 Java 实现可以将 s 声明为 volatile那么某线程初始化给 s 赋值后其它线程能立刻看到 s ! null。 为了验证上述例子存在并发问题编写测试用例如下 func TestGetInstanceV2(t *testing.T) {var wg sync.WaitGroupfor i : 0; i 100; i {wg.Add(1)go func() {GetInstanceV2()wg.Done()}()}wg.Wait()assert.True(t, s.val 1) }上述测试用例创建了 100 个 goroutine 同时调用 GetInstanceV2。 测试如下 go test -v -race -run TestGetInstanceV2 RUN TestGetInstanceV2WARNING: DATA RACE Read at 0x0000014380a8 by goroutine 9:... Previous write at 0x0000014380a8 by goroutine 8:... Goroutine 9 (running) created at:... Goroutine 8 (finished) created at:... testing.go:1312: race detected during execution of test --- FAIL: TestGetInstanceV2 (0.01s)CONT testing.go:1312: race detected during execution of test FAIL exit status 1上述报错说明了问题的存在。 Java 单例模式实现 附上 Java 的单例模式实例必须声明为 volatile public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton null) { synchronized (Singleton.class) { if (singleton null) { singleton new Singleton(); } } } return singleton; } }类似错误情形 情形一 在 The Official Golang Blog 中描述了类似的情形 Double-checked locking is an attempt to avoid the overhead of synchronization. For example, the twoprint program might be incorrectly written as: var a string var done boolfunc setup() {// 先赋值后设置 donea hello, worlddone true }func doprint() {if !done {once.Do(setup)}print(a) }func twoprint() {go doprint()go doprint() }but there is no guarantee that, in doprint, observing the write to done implies observing the write to a. This version can (incorrectly) print an empty string instead of “hello, world”. 意思是说doprint发现 done 为 true 时并不能确保它能看到 a 的值已经初始化。没有同步保证 a 先初始化再设置 done。 情形二 Another incorrect idiom is busy waiting for a value, as in: var a string var done boolfunc setup() {a hello, worlddone true }func main() {go setup()for !done {}print(a) }As before, there is no guarantee that, in main, observing the write to done implies observing the write to a, so this program could print an empty string too. Worse, there is no guarantee that the write to done will ever be observed by main, since there are no synchronization events between the two threads. The loop in main is not guaranteed to finish. 这是上一个例子的 busy waiting 变种同样不能保证 a 先初始化再设置 done。 情形三 There are subtler variants on this theme, such as this program. type T struct {msg string }var g *Tfunc setup() {t : new(T)t.msg hello, world // 1g t // 2 }func main() {go setup()for g nil {}print(g.msg) }Even if main observes g ! nil and exits its loop, there is no guarantee that it will observe the initialized value for g.msg. 上述错误更为隐晦即使 main 发现 g 已经不为 nil 了也无法保证 g.msg 已经设置也就是说不能确保代码中 语句1 和 语句2 的先后顺序。
http://www.w-s-a.com/news/884450/

相关文章:

  • 洛阳建站洛阳市网站建设视觉设计专业
  • 婚恋网站建设分析网站建设硬件需求
  • 北京做网站电话wordpress如何换图片
  • 电影网站做cpa深圳信息网
  • 单县网站建设优化大师电脑版官网
  • 番禺区住房和建设局物业网站浦东新区网站设计
  • 外贸网站外包WordPress仿牌
  • 如何设计网站logohtml5开发
  • 金坛建设银行总行网站网站开发费用如何记账
  • 贵阳企业网站设计制作湛江知名网站建设电话
  • 网站建设安全性高清效果图网站
  • 上海网站排名推广黄山公司做网站
  • 全国网站建设公司实力排名单页面网站建设
  • 网站建设方案 规划wordpress 要备案吗
  • 一个完整的网站 技术网站建设中 敬请期待.
  • 如何建一个公司的网站网上怎么推广公司产品
  • 十大旅游电子商务网站影楼网站制作
  • 深圳网站建设代理商网业打开慢的原因
  • 旅游网站经营模式在屈臣氏做网站运营
  • 做管理信息的网站com域名查询
  • 免费推广网站推荐外贸推广平台哪个好
  • 腾宁科技做网站399元全包企业校园网站建设
  • 海外医疗兼职网站建设公司取名字大全免费
  • 龙口市规划建设局网站vi设计和品牌设计的区别
  • 企业网站的总体设计网站建设评审验收会议主持词
  • 网站建设完成推广响应式网站设计开发
  • 电商网站用php做的吗网站开发流程可规划为那三个阶段
  • flash网站怎么做音乐停止深圳网站建设金瓷网络
  • 哪个网站可以做房产信息群发怎么做国内网站吗
  • 微商城网站建设公司的价格卖磁铁的网站怎么做的