zoukankan      html  css  js  c++  java
  • go 并发有趣现象和要避开的坑

    func main() {
    	count := 0
    	wg := sync.WaitGroup{}
    	wg.Add(10)
    	for i := 0; i < 10; i++ {
    		go func() {
    			defer wg.Done()
    			for j := 0; j < 100000; j++ {
    				count++
    			}
    		}()
    	}
    	wg.Wait()
    
    	fmt.Println(count)
    }
    思考一下,最后输出的 count 变量的值是多少?是不是一百万?

    输出结果

    在上述代码中,我们通过 for-loop 循环起 goroutine 进行自增,并使用了 sync.WaitGroup 来保证所有的 goroutine 都执行完毕才输出最终的结果值。

    最终的输出结果如下:




    输出的结果值不是恒定的,也就是每次输出的都不一样,且基本不会达到想象中的一百万。

    分析原因

    其原因在于 count++ 并不是一个原子操作,在汇编上就包含了好几个动作,如下:

    MOVQ "".count(SB), AX 
    LEAQ 1(AX), CX 
    MOVQ CX, "".count(SB)

    因为可能会同时存在多个 goroutine 同时读取到 count 的值为 1212,并各自自增 1,再将其写回。

    与此同时也会有其他的 goroutine 可能也在其自增时读到了值,形成了互相覆盖的情况,这是一种并发访问共享数据的错误。

    发现问题

    这类竞争问题可以通过 Go 语言所提供的的 race 检测(Go race detector)来进行分析和发现:

    编译器会通过探测所有的内存访问,监听其内存地址的访问(读或写)。在应用运行时就能够发现对共享变量的访问和操作,进而发现问题并打印出相关的警告信息。

    需要注意的一点是,go run -race 是运行时检测,并不是编译时。且 race 存在明确的性能开销,通常是正常程序的十倍,因此不要想不开在生产环境打开这个配置,很容易翻车。

     
  • 相关阅读:
    HDU1041
    HDU1005
    HDU1231
    MYSQL入门总结
    oracle性能问题排查~记一个单实例的问题
    mysql案例~关于mysql的配置文件个人见解
    数据恢复系列~恢复方案制定
    mysql架构解读~mysql的多源复制
    mysql 案例~select引起的性能问题
    遭遇Bad version number in .class file
  • 原文地址:https://www.cnblogs.com/ithubb/p/14559338.html
Copyright © 2011-2022 走看看