## **前言**
我毕业已经块10个月了,现在在一家区块链公司就职。我们主要做技术,开发公链,使用Go语言,因此就一直在学习Go语言相关知识。我觉得我可能和大多数开发者一样:以几本基础的书入门,学习下基本语法(好在Go语言是我觉得语法最简单的语言)然后再学习下channel怎么回事,goroutine怎么回事,基本上就开始上手了。从项目中也学习到了一些知识点,也踩了不少的坑。
但是我越来越发现,当你一直在做一件事的时候,那你的技术和认知也就停留在这上面了。这就导致了你写的代码永远是一个样子,用的方法也永远是一个样子,很多程序员死就是死在了这点上。当然,我自己也认识到了这一点,所以,今天开始得花点额外时间学习了。
《Go语言并发之道》这本书我从入职后就买了,一直拿他当工具书,也没有系统地看过。每当项目中遇到一些并发问题超出了认知能力我就翻翻看看,所以也就不知道什么时候用channel,什么时候用mutex等等问题。这一系列文章当做自己的读书笔记吧,同时尽量说的直白简单点,让志同道合的程序员也能相互学习和讨论。
## **规划**
我的计划是先系统地学习下Go的并发原语,然后再深入源码了解下goroutine和channel,坚持为贵,希望大家监督和支持
## **吐槽**
写到这里我可以吐槽下Go语言中文网这个文章的UI实在是太难看了么.....
## **并发问题**
很肯定地说,如果不写并发程序,那么软件bug将会减少90%....,并发程序代码容易出问题的原因在于,他的执行顺序并不一定是是按照我们的预期的那样,例如下面这段代码就可能有三种输出情况:
- 不打印任何东西
- the value is 0
- the value is 1
```go
package main
import "fmt"
func main(){
var data int
go func(){
data++
}()
if data == 0 {
fmt.Printf("the value is %d\n", data)
}
}
```
因为用`go`关键字修饰的立即执行函数并不会一定按照书写的代码顺序执行,所以`data++`到底发生在什么时候我们也不知道,导致了程序输出结果的不确定性。
## **临界区**
仔细看上面程序的问题的根本原因在于:不同的线程访问相同的变量
- main线程对`data`变量读取
- `go`关键字修饰的函数对`data`变量的写
由于这两个线程的执行顺序不确定,也就导致了程序结果输出的不确定,通常我们把不同线程共享的内存区域称为:`临界区`,在这里临界区就是`data`变量
实际上如果你用`go build -race`去编译并运行程序后,会提示你存在`竞争`,通常我们通过`加锁`来消除这种竞争(后期聊细节)
所以,从这里可以看出,多线程编程一定会存在`临界区`,而对`临界区`的访问通常是问题的根源,以后我们将会看到处理并发问题的主要思想就是管理线程对临界区的访问
有疑问加站长微信联系(非本文作者))