《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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...