《Go 語言併發之道》讀書筆記(四)

来源:https://www.cnblogs.com/dk168/archive/2022/11/22/16915333.html
-Advertisement-
Play Games

來源:blog.csdn.net/weixin_61594803 1.SQL數據脫敏實現 MYSQL(電話號碼,身份證)數據脫敏的實現 -- CONCAT()、LEFT()和RIGHT()字元串函數組合使用,請看下麵具體實現 -- CONCAT(str1,str2,…):返回結果為連接參數產生的字元 ...


今天這篇筆記我們記錄sync包下麵的Cond,Once和Pool

Cond

cond就是條件,當條件不滿足的時候等待Wait(),條件滿足後,繼續執行。 通過Signal()和Broadcast()來通知wait結束,繼續執行。我們先來看一個Signal通知的例子

func main() {

	c := sync.NewCond(&sync.Mutex{})
	queue := make([]interface{}, 0, 10)

	removeFromQueue := func(delay time.Duration) {
		time.Sleep(delay)
		c.L.Lock()
		queue = queue[1:]
		fmt.Println("Removed from queue")
		c.L.Unlock()

		c.Signal()
	}

	for i := 0; i < 10; i++ {
		c.L.Lock()
		if len(queue) == 2 {
			c.Wait()
		}

		fmt.Println("adding to the queue")
		queue = append(queue, struct{}{})
		go removeFromQueue(1 * time.Second)
		c.L.Unlock()

	}

	fmt.Printf("queue length %d", len(queue))
}

這個程式是初始化了一個隊列, 當隊列的長度是2的時候,主goroutine等待, remove goruntine會刪除隊列中的數據,然後通過Signal方法通知主goroutine結束等待,繼續執行添加。
這個程式執行的效果是這樣的

adding to the queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue

可以看到當添加了兩個後,就等待了,後面remove一個就添加一個,最後還有兩個還沒有來得及remove,主goroutine就退出了。

如果把c.Signal去掉,那麼就會報死鎖的錯誤, 因為主的goroutine等待了,子的gorountine也執行完了,就是都asleep了,就導致了報錯。

adding to the queue
adding to the queue
Removed from queue
Removed from queue
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000064050, 0x0)
        C:/Program Files/Go/src/runtime/sema.go:517 +0x152
sync.(*Cond).Wait(0xeded38?)
        C:/Program Files/Go/src/sync/cond.go:70 +0x8c
main.main()
        C:/Learn/go/helloworld/goroutinelearn/class4/main.go:30 +0x331
exit status 2

如果僅僅是通知一個等待一個,通過channel就可以做到。 如果要通知多個goroutine,那麼就需要用到Broadcast. 我們把上面的例子稍微改動一下,讓多個刪除的goroutine等待插入goroutine的通知,代碼如下所示


	c := sync.NewCond(&sync.Mutex{})
	queue := make([]interface{}, 0, 10)
	var waitgroup sync.WaitGroup

	removeFromQueue := func(delay time.Duration) {
		c.L.Lock()
		for len(queue) < 1 {
			c.Wait()
		}
		queue = queue[1:]
		fmt.Println("Removed from queue")
		waitgroup.Done()
		c.L.Unlock()

	}

	for i := 0; i < 2; i++ {
		c.L.Lock()

		go removeFromQueue(1 * time.Second)
		go removeFromQueue(1 * time.Second)

		c.L.Unlock()

	}

	waitgroup.Add(4)

	for i := 0; i < 4; i++ {
		c.L.Lock()

		fmt.Println("adding to the queue")
		queue = append(queue, struct{}{})

		c.Broadcast()

		c.L.Unlock()

	}

	waitgroup.Wait()

我們故意做成一次迴圈調用兩個刪除goroutine, 然後在刪除裡面當queue的數量為空的時候等待,
插入的時候,通過Broadcast來廣播這個消息。程式運行結果

adding to the queue
adding to the queue
adding to the queue
Removed from queue
Removed from queue
Removed from queue
adding to the queue
Removed from queue

結果可以看出,添加了之後,隨後就能刪除掉。 通知多個等待的goroutine,Broadcast還是比較有用。

Once

once 我們顧名思意就是一次, 只運行一次的意思。 這種對於只需要執行一次的功能會非常有用。
看下麵的示例代碼

	var count int
	var lock sync.RWMutex

	increment := func() {
		lock.Lock()
		count++
		lock.Unlock()
	}

	decrement := func() {
		lock.Lock()
		fmt.Printf(" call decrement \n")
		count--
		lock.Unlock()
	}

	var once sync.Once

	var increments sync.WaitGroup
	increments.Add(100)
	for i := 0; i < 100; i++ {
		go func() {
			defer increments.Done()
			//increment()
			once.Do(increment)
		}()
	}

	once.Do(decrement)

	increments.Wait()

	fmt.Printf("Count is %d \n", count)


代碼的輸出為“Count is 1”, 我們通過once.Do 調用了increment 100次, 調用了 decrement 1次,但是實際上increment只被調用了一次。 once.Do 是保證它只被調用一次,不是細到方法,不是說一個方法調用一次,而是所有的都只調用一次。

Pool

談到池,我們想到最多的就是線程池或者資料庫連接池, 也比較好理解,就是創建一個資源比較耗時的時候,我們通過池來緩存一些資源,這樣就不用每次都創建。
看下麵示例代碼


	connPool := warmServiceConnCache()
	connPool.Put(connPool.New())
	connPool.Put(connPool.New())

	for i := 1; i < 10; i++ {
		conn1 := connPool.Get().(*Conncetor)
		conn1.connect()
		connPool.Put(conn1)
	}
}

func warmServiceConnCache() *sync.Pool {
	p := &sync.Pool{
		New: connectToService,
	}

	return p
}

type Conncetor struct {
}

func (connector *Conncetor) connect() {
	fmt.Println(" connecting")
}

func connectToService() interface{} {
	fmt.Println(" creating new instance")
	return new(Conncetor)
}

我們通過warmServiceConnCache 來返回一個Pool, 然後往這個Pool 中放入兩個Connector,
通過Pool.Get()方法拿到創建的Connector, 調用了10次, 都是從Pool裡面拿對象,而不需要創建10次,節省了資源。
程式的輸出結果如下

 creating new instance
 creating new instance
 connecting
 connecting
 connecting
 connecting
 connecting
 connecting
 connecting
 connecting
 connecting

總結

這三個類,對於寫併發程式還是很有作用,光看不是很理解怎麼使用,通過敲代碼,修改代碼,能夠對他們的用法有比較清晰的理解。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 學習:SQL 語句到結構體的轉換 | Go 語言編程之旅 (eddycjy.com) 目標:SQL表轉換為Go語言結構體 可以線上體驗這個過程:SQL生成GO語言結構體 - 支持批量處理 (tl.beer) MySQL資料庫中的表結構,本質上是SQL語句。 CREATE TABLE `USER`( ...
  • 規則引擎 Drools 全套代碼及資料全部完整提供,點此處下載 1. 問題引出 現有一個線上申請信用卡的業務場景,用戶需要錄入個人信息,如下圖所示: 通過上圖可以看到,用戶錄入的個人信息包括姓名、性別、年齡、學歷、電話、所在公司、職位、月收入、是否有房、是否有車、是否有信用卡等。錄入完成後點擊申請按 ...
  • 1、static修飾局部變數 在函數體內,只初始化一次,被static聲明過的局部變數在調用過程中值不變。原因:在任意函數內定義局部變數,存儲線上程中的棧區,出函數時自動摧毀,所以在每次調用這個函數時,局部變數的初始值都為定義的值在進行運算。static在修飾局部變數時,存儲在靜態區,函數返回時值保 ...
  • 前面幾篇文章對 Yarn 基本架構、程式基礎庫、應用設計方法等進行了介紹。之後幾篇將開始對 Yarn 核心組件進行剖析。 ResourceManager(RM)是 Yarn 的核心管理服務,負責集群管理、任務調度、狀態機管理等,本篇將對 RM 總體架構進行介紹。 ...
  • 哈夫曼編碼應用 問題描述 ​ 給定字元串,將每個不同的字元的哈夫曼編碼表示並輸出其哈夫曼編碼,並再將其哈夫曼編碼還原回字元串 分析一下 構建哈夫曼樹 ​ 使用靜態鏈表,先將所有的結點關係全部清零,再進行結點和相應權值的賦值,遍歷後n-1個結點 (新根),從n個結點中選兩個最小的權值了合成一棵樹,並將 ...
  • 一.小結 1.一個boolean變數可以存儲值true或false 2.關係運算符(<,<=,==,!=,>,>=)和數值及字元一起運算 3.布爾運算符&&,|| ,| 和 ^對布爾值和布爾變數進行計算 4.當對p1&&p2求值時,java先求p1的值,如果p1為true,再對p2求值;如果p1為f ...
  • 迭代器的功能: 提供一種統一的方式,來透明的遍歷容器 理解 begin()方法,end()方法, ++ , * 的用處 其中 C++11 中提供的foreach的方式,其底層還是通過迭代器來進行遍歷的. #include <iostream> using namespace std; class M ...
  • 1 概述 ArrayList實現了List介面,是 順序容器,允許放入null元素 有一個容量(capacity),表示底層數組的實際大小。如果容量不足,容器會 自動增大底層數組的大小 支持泛型,泛型擦除後,容器的元素都是 Object類型 ArrayList沒有實現同步(synchronized) ...
一周排行
    -Advertisement-
    Play Games
  • MQTTnet 是一個高性能的MQTT類庫,支持.NET Core和.NET Framework。 MQTTnet 原理: MQTTnet 是一個用於.NET的高性能MQTT類庫,實現了MQTT協議的各個層級,包括連接、會話、發佈/訂閱、QoS(服務質量)等。其原理涉及以下關鍵概念: MqttCli ...
  • 在WPF中,源屬性(Source Property)指的是提供數據的屬性,通常是數據模型或者其他控制項的屬性,而目標屬性(Target Property)則是數據綁定的目標,通常是綁定到控制項的屬性,例如TextBlock的Text屬性。數據綁定將源屬性的值自動更新到目標屬性中。 主要包含以下幾個事件: ...
  • async/await 是 C# 中非同步編程的關鍵特性,它使得非同步代碼編寫更為簡單和直觀。下麵深入詳細描述了 async/await 的使用場景、優點以及一些高級使用方法,並提供了相應的實例源代碼。 使用場景: I/O 操作: 非同步編程特別適用於涉及 I/O 操作(如文件讀寫、網路請求等)的場景。在 ...
  • 使用過office的visio軟體畫圖的小伙伴都知道,畫圖軟體分為兩部分,左側圖形庫,存放各種圖標,右側是一個畫布,將左側圖形庫的圖標控制項拖拽到右側畫布,就會生成一個新的控制項,並且可以自由拖動。那如何在WPF程式中,實現類似的功能呢?今天就以一個簡單的小例子,簡述如何在WPF中實現控制項的拖拽和拖動,... ...
  • 1、Blazor Hybrid簡介 Blazor Hybrid 使開發人員能夠將桌面和移動本機客戶端框架與 .NET 和 Blazor 結合使用。在 Blazor Hybrid 應用中,Razor 組件在設備上是本機運行的。 這些組件通過本地互操作通道呈現到嵌入式 Web 視圖控制項。 組件不在瀏覽器 ...
  • 除了內置的數據集,scikit-learn還提供了隨機樣本的生成器。通過這些生成器函數,可以生成具有特定特性和分佈的隨機數據集,以幫助進行機器學習演算法的研究、測試和比較。 目前,scikit-learn庫(v1.3.0版)中有20個不同的生成樣本的函數。本篇重點介紹其中幾個具有代表性的函數。 1. ...
  • 從0到1,手把手帶你開發截圖工具ScreenCap------002實現通過文件對話框,選擇合適的文件夾,自定義預設的圖片保存位置,簡單易學 ...
  • 每次談到容器的時候,除了Docker之外,都會說起 Kubernetes,那麼什麼是 Kubernetes呢?今天就來一起學快速入門一下 Kubernetes 吧!希望本文對您有所幫助。 Kubernetes,一種用於管理和自動化雲中容器化工作負載的工具。 想象一下你有一個管弦樂隊,將每個音樂家視為 ...
  • 目錄 基本說明 安裝 Nginx 部署 VUE 前端 部署 Django 後端 Django admin 靜態文件(CSS,JS等)丟失的問題 總結 1. 基本說明 本文介紹了在 windows 伺服器下,通過 Nginx 部署 VUE + Django 前後端分離項目。本項目前端運行在 80 埠 ...
  • 從0到1,手把手帶你開發截圖工具ScreenCap------003實現最小化程式到托盤運行,- 為了方便截圖乾凈,實現最小化程式到托盤運行,簡潔,勿擾,實現最小化程式到托盤運行, 實現托盤菜單功能,實現回顯主窗體, 實現托盤開始截屏, 實現氣泡信息提示,實現托盤程式提示,實現托盤退出程式, 封裝完... ...