Go併發編程之美-條件變量

一、前言

go語言類似Java JUC包也提供了一些列用於多線程之間進行同步的措施,比如低級的同步措施有 鎖、CAS、原子變量操作類。相比Java來說go提供了獨特的基於通道的同步措施。本節我們先來看看go中與鎖相關的條件變量

二、條件變量

在java中條件變量是與具體的鎖想關聯的,在go中也是這樣的。

package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    counter int                   //計數器
    wg      sync.WaitGroup        //信號量
    lock    sync.Mutex            //互斥鎖
    cond    = sync.NewCond(&lock) //條件變量
)

func main() {

    //1.獲取鎖
    cond.L.Lock()
    fmt.Println("main  thread got lock  ")

    //2.開啓線程執行一些事情
    go doSomething()

    //3.用與鎖關聯的條件變量的wait方法
    fmt.Println("main thread begin wait  ")
    cond.Wait()
    fmt.Println("main thread end wait  ")

    //4.釋放鎖
    cond.L.Unlock()
    fmt.Println("main thread release lock  ")

}

func doSomething() {
    //2.1激活阻塞到條件變量的wait方法的一個線程

    time.Sleep(time.Second * 2)
    //2.2獲取鎖
    fmt.Println("sub thread begin get lock ")
    //cond.L.Lock()
    fmt.Println("sub thread  got lock ")

    time.Sleep(5 * time.Second)
    cond.Signal()

    //2.3釋放鎖
    //cond.L.Unlock()
    fmt.Println("sub thread release lock ")

}

  • go中使用sync.NewCond(&lock)創建一個條件變量,其中lock可以是互斥鎖或者讀寫鎖
  • 主線程線程先獲取了lock鎖(cond.L就是lock變量),然後開啓了子線程,然後調用了條件變量的wait方法,main線程然後被阻塞,並且main線程會釋放獲取的lock鎖。
  • 子線程則首先嚐試獲取lock鎖,如果是在main線程執行條件變量的wait前嘗試獲取鎖,則子線程會被阻塞,等main線程執行到wait後,main函數釋放鎖後,子線程會獲取鎖成功。
  • 子線程獲取鎖成功後,會先休眠5s然後釋放鎖,然後調用條件變量的signal方法,這時候main線程則會重新獲取到鎖,然後從wait返回。

需要注意的是調用條件變量的signal方法的線程在調用該方法前,獲取關聯的lock鎖這個並不是必須的,讀者可以註釋獲取和釋放鎖代碼,也是OK的。

與Java中類似調用條件變量的signal會激活一個線程,調用Broadcast會激活所有阻塞到條件變量wait方法的線程。

另外需要注意,一般調用線程應該使用循環檢查方式調用條件變量的wait方法,以避免虛假喚醒等問題。

三、總結

go中條件變量與Java中條件變量類似,但是也有不同,相同在於條件變量都是與鎖關聯的,並且只有當線程獲取到鎖後纔可以調用其關聯的條件變量的wait方法,否則會拋出異常,另外當線程阻塞到wait方法後,當前線程會釋放已經獲取的鎖。不同在於Java中只有當線程獲取到鎖後纔可以調用其關聯的條件變量的signal方法,否則會拋出異常,但是在go中調用線程調用signal前獲取鎖不是必須的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章