併發技術5:死鎖問題

尹成發表於2018-11-16

1. 同一個goroutine中,使用同一個 channel 讀寫

package main
func main(){
    ch:=make(chan int)  //這就是在main程裡面發生的死鎖情況
    ch<-6   //  這裡會發生一直阻塞的情況,執行不到下面一句
    <-ch
}

這是最簡單的死鎖情況
看執行結果
這裡寫圖片描述

1. 2個 以上的go程中, 使用同一個 channel 通訊。 讀寫channel 先於 go程建立。

 package main

func main(){
    ch:=make(chan int)
    ch<-666    //這裡一直阻塞,執行不到下面
    go func (){
        <-ch  //這裡雖然建立了子go程用來讀出資料,但是上面會一直阻塞執行不到下面
    }()
}

這裡如果想不成為死鎖那匿名函式go程就要放到ch<-666這條語句前面
這裡寫圖片描述

3. 2個以上的go程中,使用多個 channel 通訊。 A go 程 獲取channel 1 的同時,嘗試使用channel 2, 同一時刻,B go 程 獲取channel 2 的同時,嘗試使用channel 1

package main
func main()  {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go func() {    //匿名子go程
        for {
            select {    //這裡互相等對方造成死鎖
            case <-ch1:   //這裡ch1有資料讀出才會執行下一句
                ch2 <- 777
            }
        }
    }()
    for {         //主go程
        select {
        case <-ch2 : //這裡ch2有資料讀出才會執行下一句
            ch1 <- 999
        }
    }
}

第三種是互相等對方造成死鎖

4.注意讀寫模式的鎖定不要互相阻塞

  • 隱形死鎖:系統的兩個或多個任務之間互相阻塞對方,形成事實上的死鎖局面,然而只要有可執行的協程,編譯器就不會顯式地報死鎖錯誤——這就是隱形死鎖;
  • 開發中真正可怕的不是顯式的死鎖,而是隱形死鎖;
func main() {

	var rwm09 sync.RWMutex
	ch := make(chan int, 0)

	//子協程負責寫入
	go func() {
		//連鎖都搶不到555...
		rwm09.Lock()
		ch <- 123
		rwm09.Unlock()
	}()

	go func() {
		//本協程負責讀出
		rwm09.RLock()
		//只要讀不到內容就永遠阻塞
		x := <- ch
		fmt.Println("讀到",x)
		rwm09.RUnlock()
	}()

	for {
		//通知垃圾回收器來清理垃圾(即使不叫也會定時清理)
		runtime.GC()
	}

}

學院Go語言視訊主頁
https://edu.csdn.net/lecturer/1928

[清華團隊帶你實戰區塊鏈開發]

(https://ke.qq.com/course/344443?tuin=3d17195d)
掃碼獲取海量視訊及原始碼 QQ群:721929980

在這裡插入圖片描述

相關文章