Golang 入门 : 竞争条件
笔者在前文《Golang 入门 : 理解并发与并行》和《Golang 入门 : goroutine(协程)》中介绍了 Golang 对并发的原生支持以及 goroutine 的用法。本文我们来聊聊并发与并行带来的一些副作用。
并行编程之所以难道较高,根本的原因是需要处理共享资源的同步访问。比如在 Golang 中如果两个或者多个 goroutine 在没有互相同步的情况下,访问某个共享的资源,并试图同时读和写这个资源,就处于相互竞争的状态,这种情况被称作竞争条件(race candition)。竞争条件的存在是让并发程序变得复杂的地方,十分容易引起潜在问题。对一个共享资源的读和写操作必须是原子化的,换句话说,同一时刻只能有一个 goroutine 对共享资源进行读和写操作。
goroutine 引入的竞争条件
让我们来通过下面的 demo 来观察 goroutine 引入的竞争条件,为了让观察结果明显,我们采取了一些极端措施:
package main import (
"sync"
"fmt"
"runtime"
) var(
// counter是所有goroutine都要增加其值的变量
counter int // wg用来等待程序结束
wg sync.WaitGroup
) // main是所有Go程序的入口
func main(){
runtime.GOMAXPROCS()
// 计数加2,表示要等待两个goroutine
wg.Add() // 创建两个goroutine
go incCounter()
go incCounter() // 等待goroutine结束
wg.Wait()
fmt.Println("Final Counter:", counter)
} // incCounter增加包里counter变量的值
func incCounter(id int){
// 在函数退出时调用Done来通知main函数工作已经完成
defer wg.Done() for count := ; count < ; count++{
// 捕获counter的值
value := counter // 当前goroutine从线程退出,并放回到队列
runtime.Gosched() // 增加本地value变量的值
value++ // 将该值保存回counter
counter = value
}
}
运行上面的代码,输出结果如下:
Final Counter:
上面的程序中会对变量 counter 会进行 4 次读和写操作,每个 goroutine 执行两次。但是,程序终止时,counter 变量的值为 2。我们可以通过下面的图解来理解该程序的执行过程(此图来自互联网):

每个 goroutine 都会覆盖另一个 goroutine 的工作。这种覆盖发生在 goroutine 切换的时候。每个 goroutine 创造了一个 counter 变量的副本,之后就切换到另一个 goroutine。当 这个 goroutine 再次运行的时候,counter 变量的值已经改变了,但是 goroutine 并没有更新自己的那个副本的值,而是继续使用这个副本的值,用这个值递增,并存回 counter 变量,结果覆盖了另一个 goroutine 完成的工作。 下面是对程序执行过程的解释:
// 创建两个 goroutine
go incCounter()
go incCounter()
程序中通 go 关键字和 incCounter 函数创建了两个 goroutine。在 incCounter 函数内部对变量 counter 进行了读和写操作,而 counter 变量是这个示例程序里的共享资源。每个 goroutine 都会先读出这个 counter 变量的值,并把 counter 变量的副本存入一个叫作 value 的本地变量。之后 incCounter 函数对 value 变量加 1,并最终将这个新值存回到 counter 变量。incCounter 函数在对本地变量 value加 1 前调用了 runtime 包的 Gosched 函数,这个调用会将 goroutine 从当前线程退出,给其他 goroutine 运行的机会。在两次操作中间这样做的目的是强制调度器切换两个 goroutine,以便让竞争条件的效果变得更明显。
如果不是我们通过调用 Gosched 函数让竞争条件的效果变得明显,那么多次运行这段程序输出的 counter 值很可能是不一样的,会是 2,3,4 中的一个值。这种情况下导致的问题往往非常难以定位。
和其它编程语言一样,Golang 提供了原子函数和锁等机制来解决同步问题。但是使用这些机制并不会使并发编程变得更简单。接下来笔者将介绍 Golang 中提供的 channel(通道)功能,看它是如何以简洁的方式解决同步问题的。
参考:
《Go语言实战》
Golang 入门 : 竞争条件的更多相关文章
- Golang 入门 : channel(通道)
笔者在<Golang 入门 : 竞争条件>一文中介绍了 Golang 并发编程中需要面对的竞争条件.本文我们就介绍如何使用 Golang 提供的 channel(通道) 消除竞争条件. C ...
- Java程序员的Golang入门指南(下)
Java程序员的Golang入门指南(下) 4.高级特性 上面介绍的只是Golang的基本语法和特性,尽管像控制语句的条件不用圆括号.函数多返回值.switch-case默认break.函数闭包.集合 ...
- Java程序员的Golang入门指南(上)
Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如 ...
- Golang入门(3):一天学完GO的进阶语法
摘要 在上一篇文章中,我们聊了聊Golang中的一些基础的语法,如变量的定义.条件语句.循环语句等等.他们和其他语言很相似,我们只需要看一看它们之间的区别,就差不多可以掌握了,所以作者称它们为&quo ...
- Golang入门(2):一天学完GO的基本语法
摘要 在配置好环境之后,要研究的就是这个语言的语法了.在这篇文章中,作者希望可以简单的介绍一下Golang的各种语法,并与C和Java作一些简单的对比以加深记忆.因为这篇文章只是入门Golang的第二 ...
- 从头认识java-17.4 具体解释同步(2)-具体解释竞争条件
这一章节我们来具体讨论一下竞争条件. 1.为什么会引起竞争条件? 因为操作缺失原子性. 2.什么是原子性? 所谓原子操作是指不会被线程调度机制打断的操作:这样的操作一旦開始,就一直运行到结束.中间不会 ...
- 理解竞争条件( Race condition)漏洞
这几天一个叫做"Dirty COW"的linux内核竞争条件漏洞蛮火的,相关公司不但给这个漏洞起了个洋气的名字,还给它设计了logo(见下图),首页,Twitter账号以及网店.恰 ...
- Java多线程中的竞争条件、锁以及同步的概念
竞争条件 1.竞争条件: 在java多线程中,当两个或以上的线程对同一个数据进行操作的时候,可能会产生“竞争条件”的现象.这种现象产生的根本原因是因为多个线程在对同一个数据进行操作,此时对该数据的操作 ...
- linux c编程:进程控制(二)_竞争条件
前面介绍了父子进程,如果当多个进程企图对共享数据进行处理.而最后的结果又取决于进程运行的顺序时,就认为发生了竞争关系.通过下面的例子来看下 在这里标准输出被设置为不带缓冲的,于是父子进程每输出一个字符 ...
随机推荐
- JavaEE JDBC PreparedStatement
PreparedStatement @author ixenos PreparedStatement工作原理 注意:虽然mysql不支持PreparedStatement优化,但依然有预编译的实现! ...
- c/c++ 位域的概念
位域 有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C语言又提供了一种数 ...
- MSDN 同步部分 个人笔记
(在知乎看到轮子哥说,掌握了MSDN上的并发部分 和 线程与进程部分就可以掌握所有语言的多线程编程,我在网上翻了一下并没有中文版,所以决定自己翻译一下...) 目录: 线程之间协同运行的方式有许多种, ...
- Eclipse错误:Syntax error on tokens, delete these tokens问题解决
错误:Syntax error on tokens, delete these tokens 出现这样的错误一般是括号.中英文字符.中英文标点.代码前面的空格,尤其是复制粘贴的代码,去掉即可.
- dubbo服务的group和version
group 当一个接口有多种实现时,可以用group区分 <!-- dubbo group 使用示例 --> <bean id="demoA" class=&qu ...
- 我怎么在AD里面找到已经改名的Administrator账户?
近期有博友问我一个问题,他是一个企业里面的IT管理员,他非常苦恼.他是一个新手,之前管理员交接的时候,没有交接更改的管理员username和password.他如今不知道哪个才是系统之前内置的admi ...
- HDU - 3584 Cube (三维树状数组 + 区间改动 + 单点求值)
HDU - 3584 Cube Time Limit: 1000MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u Subm ...
- android学习笔记NO.5
Intent 能够理解为信使 由Intent来协助完毕android各个组件之间的通讯. 感觉基础薄弱就又复习了一遍基础知识,整理了一遍! gen 保存自己主动生成的R资源目录 gen->com ...
- python 【目录】
python[目录] python[第一篇]初识python python[第二篇]python基本数据类型 python[第三篇]python函数基础
- 介绍Android拍照,录像开发的相关东东
Android下相机有自带的照片功能,可是作为开发人员,我们需要更为深层次的知道,怎么用,以及相关原理,这里我就这方面的学习,写一下心得,供博友参考. 第一种:调用系统自带相机界面. 这时我们在布局文 ...