## 競爭條件
當我們能夠沒有辦法自信地確認一個事件是在另一個事件的前面或者后面發生的話,就說明x和y這兩個事件是并發的。
## 并發安全
一個函數在線性程序中可以正確地工作。如果在**并發**的情況下,這個函數依然可以**正確地工作**的話,那么我們就說這個函數是并發安全的,并發安全的函數不需要額外的同步工作。我們可以把這個概念概括為一個特定類型的一些方法和操作函數,對于某個類型來說,如果其所有可訪問的**方法和操作都是并發安全**的話,那么類型便是**并發安全的。**
## 哪些是并發不安全的
導出包級別的函數一般情況下都是并發安全的。由于**package級的變量沒法被限制在單一的gorouine**,所以修改這些變量“必須”使用互斥條件。
也就是說,包級別的變量是并發不安全的,包括所有普通類型和slice,map,數組.
## 競爭條件
**競爭條件指的是程序在多個goroutine交叉執行操作時,沒有給出正確的結果**。競爭條件是很惡劣的一種場景,因為這種問題會一直潛伏在你的程序里,然后在非常少見的時候蹦出來,或許只是會在很大的負載時才會發生,又或許是會在使用了某一個編譯器、某一種平臺或者某一種架構的時候才會出現。這些使得**競爭條件帶來的問題非常難以復現而且難以分析診斷**。
## 產生競爭條件的原因
所有對數據進行的操作,都會先拿到這個數據本身的值,然后再本身的值上面進行操作. 非并發情況下,肯定是順序執行的.但是在并發條件下A先讀S的值,此時B在A的后面也讀到S的值了,然后B在S上+100,A也在S上+100.然后S的值就是100.因為B對S的操作丟失了.
```
S := 0
A讀 //S = 0
B讀 //S = 0
B寫: S += 100 //S = 100
A寫: S += 100 //S = 100
S最后就等于100了,因為A和B都不是在對方已經+100的基礎上進行+100的.它們讀到的都是0.
```
## 數據競爭(非常重要的概念)
無論任何時候,只要有**兩個或以上goroutine并發訪問同一變量**,且至少其中的一個是**寫操作**的時候就會發生數據競爭。
## 一定要避免數據競爭
**一個好的經驗法則是根本就沒有什么所謂的良性數據競爭(不要心存幻想)**。
## 避免數據競爭三種方式
### 1.不去寫變量
對于共享數據,所有的并發只讀它,而不去修改它.(但是很多業務就是要寫啊,所以這個方式不好)
### 2.避免多個goroutine訪問變量
只能使用一個channel來發送給指定的goroutine請求來查詢更新變量。這也就是Go的口頭禪“不要使用共享數據來通信;使用通信來共享數據”。一個提供對一個指定的變量通過channel來請求的goroutine叫做這個變量的監控(monitor)goroutine。
下面的例子可以看的很清除,對于共享數據,專門使用一個goroutine來監控它數據操作的情況,所有的操作都是有順序的.
**這里的秘密就在于,讀是可以隨便讀,而寫則通過channel來進行線性化.這樣就避免了數據競爭**.
~~~
package bank
var deposits = make(chan int) // send amount to deposit
var balances = make(chan int) // receive balance
func Deposit(amount int) { deposits <- amount }
func Balance() int { return <-balances }
func teller() {
var balance int // balance is confined to teller goroutine
for {
select {
case amount := <-deposits:
balance += amount
case balances <- balance:
}
}
}
func init() {
go teller() // start the monitor goroutine
}
~~~
即使**當一個變量無法在其整個生命周期內被綁定到一個獨立的goroutine**,綁定依然是并發問題的一個解決方案。例如在一條流水線上的goroutine之間共享變量是很普遍的行為,在這兩者間會通過channel來傳輸地址信息。如果流水線的每一個階段都能夠避免在將變量傳送到下一階段后再去訪問它,那么對這個變量的所有訪問就是線性的。其效果是變量會被綁定到流水線的一個階段,傳送完之后被綁定到下一個,以此類推。這種規則有時被稱為串行綁定。
怎么理解上面的話呢?就是可能一個變量是在函數中的,在函數內部使用了并發程序去操作它,那么依然是可以通過channel來將共享的變量綁定在一個channel上.那么這樣也是線性的.只要是在往這個channel發送該變量之前沒有再去使用它.
### 3.互斥
允許很多goroutine去訪問變量,但是在同一個時刻最多只有一個goroutine在訪問。這種方式被稱為“互斥”.
- 基本語法
- 申明變量
- 常量
- 數據類型
- 強制類型轉換
- 獲取命令行參數
- 指針
- 概述
- new函數
- 函數
- 概述
- 不定參數類型
- 有返回值
- 函數類型
- 回調函數
- 匿名函數和閉包
- 延遲調用defer
- 工程管理
- 工作區
- src,pkg和bin目錄
- 復合類型
- 概述
- 數組
- 概述
- 聲明并初始化
- 拷貝傳值
- slice
- 概述
- 創建切片
- 切片截取
- 切片和底層數組的關系
- slice常用方法
- 切片做函數參數
- map
- 概述
- map操作
- 結構體
- 概述
- 結構體初始化
- 結構體比較
- 結構體作為函數參數
- 結構體前加&
- 面向對象
- 概述
- 匿名組合
- 方法
- 值語義和引用語義
- 方法集
- 方法的繼承
- 方法重寫
- 方法值
- 接口
- 接口定義和實現
- 多態的表現
- 接口繼承
- 接口轉換
- 空接口
- 類型斷言
- 異常處理
- error接口
- panic
- recover
- 文本文件處理
- 字符串操作
- 正則表達式
- json處理
- 文件操作
- 標準設備文件操作
- 并發編程
- 概述
- 并發和并行
- go語言并發優勢
- goroutine
- goroutine概述
- 創建goroutine
- 主協程先退出
- runtime包
- Gosched
- Goexit
- GOMAXPROCE
- channel
- 多資源競爭
- channel類型
- 無緩沖channel
- 有緩沖channel
- 關閉channel
- 單向channel
- 單向channel特性
- 定時器
- Timer
- Ticker
- select
- select作用
- 超時
- sync
- 競爭狀態
- 網絡編程
- 網絡概述
- 網絡協議
- 分層模型
- 網絡分層架構
- 層與協議
- 每層協議的功能
- 鏈路層
- 網絡層
- 傳輸層
- 應用層
- socket編程
- 組合和繼承
- 注意事項
- 細節
- go語言實現隊列
- google工程師golang
- 基礎語法
- 內建容器
- 面向"對象"
- 依賴管理
- 面向接口
- 函數式編程
- 錯誤處理和資源管理
- 測試與性能調優
- goroutine
- channel
- golang問題集
- 斷言和類型轉換
- Go語言圣經
- 入門
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整數
- 浮點數
- 復數
- 布爾型
- 字符串
- 常量
- 復合數據類型
- 數組
- slice
- map
- 結構體
- json
- 文本和HTML模板
- 函數
- 函數聲明
- 錯誤
- 函數值
- 匿名函數
- defer
- panic
- recover
- 方法
- 方法聲明
- 指針對象的方法
- 封裝
- 接口
- 說明
- 接口是合約
- 實現接口的條件
- 接口值
- 類型斷言
- 通過類型斷言詢問行為
- 類型開關
- Goroutines和Channels
- 協程
- channels
- 無緩沖channel
- 串聯的channel
- 有緩沖channel
- 并發的循環
- select多路復用
- 并發的退出
- 并發問題的自我思考
- 基于共享變量的并發
- 競爭條件
- 互斥鎖
- 讀寫鎖
- 內存同步
- sync.Once
- 協程和線程
- 包和工具
- 測試
- 反射
- 什么是反射
- 為什么需要反射
- reflect.Type和reflect.Value
- 通過reflect.Value修改值
- 底層編程