《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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...