大道如青天,協程來通信,Go lang1.18入門精煉教程,由白丁入鴻儒,Go lang通道channel的使用EP14

来源:https://www.cnblogs.com/v3ucn/archive/2022/08/30/16640477.html
-Advertisement-
Play Games

眾所周知,Go lang的作用域相對嚴格,數據之間的通信往往要依靠參數的傳遞,但如果想在多個協程任務中間做數據通信,就需要通道(channel)的參與,我們可以把數據封裝成一個對象,然後把這個對象的指針傳入某個通道變數中,另外一個協程從這個通道中讀出變數的指針,並處理其指向的記憶體對象。 通道的聲明與 ...


眾所周知,Go lang的作用域相對嚴格,數據之間的通信往往要依靠參數的傳遞,但如果想在多個協程任務中間做數據通信,就需要通道(channel)的參與,我們可以把數據封裝成一個對象,然後把這個對象的指針傳入某個通道變數中,另外一個協程從這個通道中讀出變數的指針,並處理其指向的記憶體對象。

通道的聲明與創建

  

package main  
  
import "fmt"  
  
func main() {  
	var a chan int  
	if a == nil {  
		fmt.Println("通道是空的, 不能使用,需要先創建通道")  
		a = make(chan int)  
		fmt.Printf("數據類型是: %T", a)  
	}  
}


這裡註意,通道聲明之後還需要進行創建。

也可以通過海象操作符聲明並創建:

package main  
  
import "fmt"  
  
func main() {  
	  
	a := make(chan int)  
  
	fmt.Printf("數據類型是: %T", a)  
  
}

程式返回:

數據類型是: chan int%

如此,一個類型為整形的通道就創建好了。

此外,通道是引用數據類型:

package main  
  
import (  
	"fmt"  
)  
  
func main() {  
	ch1 := make(chan int)  
	fmt.Printf("%T,%p\n", ch1, ch1)  
  
	test1(ch1)  
  
}  
  
func test1(ch chan int) {  
	fmt.Printf("%T,%p\n", ch, ch)  
}

程式返回:

chan int,0x1400010e060  
chan int,0x1400010e060

可以看到,在test1函數內和main函數內通道的地址是一樣的,所以他們指向的都是同一個通道。

通道的使用

通道創建之後,即可以在協程之間充當橋梁:

package main  
  
import "fmt"  
  
func job(ch1 chan int) {  
  
	ch1 <- 1  
  
}  
  
func main() {  
  
	ch1 := make(chan int)  
  
	fmt.Println(ch1)  
  
	go job(ch1)  
  
	data := <-ch1 // 從ch1通道中讀取數據  
	fmt.Println("data-->", data)  
	fmt.Println("main。。over。。。。")  
}

這裡我們聲明一個函數job,把通道作為參數傳遞進去,註意這裡參數類型除了聲明通道本身以外,還得聲明通道具體的數據類型。

隨後在main函數中,可以理解為主協程,創建通道ch1,執行開啟協程任務job,在job函數內,往通道內傳遞數字1

接著,主協程獲取通道內由job協程傳遞的數據:

0x1400006a060  
data--> 1  
main。。over。。。。

藉此,就完成了數據的傳遞。

這裡需要註意通道的調用語法:

data := <- a // 讀取通道  
a <- data // 寫入通道

同步阻塞

這裡需要註意的是,通道無論是寫入還是讀取,都是同步阻塞機制。即當有協程對通道進行操作的時候,其他協程都處於“等待”狀態,說白了,就是在“排隊”,在之前的一篇:併發與並行,同步和非同步,Go lang1.18入門精煉教程,由白丁入鴻儒,Go lang併發編程之GoroutineEP13,我們要麼通過sync.WaitGroup來阻塞主協程,或者通過time.Sleep(time.Second)方法來阻塞,就是怕主協程提前執行完,早成子協程來不及執行。

而通道的出現,就間接幫我們實現了“阻塞”主協程的目的。

比如,多個協程任務操作一個變數:

package main  
  
import (  
	"fmt"  
)  
  
func job1(number int, squareop chan int) {  
	sum := 20  
	sum += number  
	squareop <- sum  
}  
  
func job2(number int, cubeop chan int) {  
	sum := 10  
	sum += number  
	cubeop <- sum  
}  
func main() {  
	number := 0  
	ch1 := make(chan int)  
	ch2 := make(chan int)  
	go job1(number, ch1)  
	go job2(number, ch2)  
	num1, num2 := <-ch1, <-ch2  
	fmt.Println("Final output", num1+num2)  
}

這裡job1和job2兩個協程任務同時非同步執行,操作number變數,累加後往通道中寫入,程式返回:

Final output 30

理論上,如果是併發執行,返回值應該是20或者10,但由於通道的存在,造成協程任務阻塞,變回了同步執行,所以返回了30。

同時,我們需要註意死鎖問題,如果一個協程任務在一個通道上發送數據,那麼其他的協程任務應該接收數據,如果這種情況不發生,那麼程式將在運行時出現死鎖。

換句話說,你發送了,就得有人接收,只發不接,或者只收不發,都會變成死鎖。

此外,協程任務可以通過close(ch)方法來關閉通道:

package main  
  
import (  
	"fmt"  
)  
  
func job(ch1 chan int) {  
	// 發送方:3條數據  
	for i := 0; i < 3; i++ {  
		ch1 <- i //將i寫入通道中  
	}  
	close(ch1) //將ch1通道關閉了。  
}  
  
func main() {  
	ch1 := make(chan int)  
	go job(ch1)  
	/*  
		子goroutine,寫出數據3個  
				每寫一個,阻塞一次,主程式讀取一次,解除阻塞  
  
		主goroutine:迴圈讀  
				每次讀取一個,堵塞一次,子程式,寫出一個,解除阻塞  
  
		發送發,關閉通道的--->接收方,接收到的數據是該類型的零值,以及false  
	*/  
	//主程式中獲取通道的數據  
	for {  
  
		v, ok := <-ch1 //其他goroutine,顯示的調用close方法關閉通道。  
		if !ok {  
			fmt.Println("已經讀取了所有的數據,", ok)  
			break  
		}  
		fmt.Println("取出數據:", v, ok)  
	}  
  
	fmt.Println("main...over....")  
}

這裡將0到2寫入chl通道,然後關閉通道。主函數里有一個死迴圈。類似while,它輪詢通道是否在發送數據後,使用變數ok進行判斷。如果ok是假的,則意味著通道關閉,因此迴圈結束,否則將會繼續進行無限輪詢。

select關鍵字

select 是 Go lang裡面的一個流程式控制制結構,和switch關鍵字差不多,但是select會隨機執行一個可運行的通道通信,如果沒有通道通信可運行,它將阻塞,直到有通道通信可運行:

package main  
  
import (  
	"fmt"  
	"time"  
)  
  
func job(ch1 chan int) {  
  
	time.Sleep(2 * time.Second)  
	ch1 <- 200  
  
}  
  
func main() {  
  
	ch1 := make(chan int)  
	ch2 := make(chan int)  
  
	go job(ch1)  
	go job(ch2)  
  
	select {  
	case num1 := <-ch1:  
		fmt.Println("ch1中取數據。。", num1)  
	case num2, ok := <-ch2:  
		if ok {  
			fmt.Println("ch2中取數據。。", num2)  
		} else {  
			fmt.Println("ch2通道已經關閉。。")  
		}  
  
	}  
}

這裡select會隨機選擇一個可運行的通道通信邏輯,可能是ch1通道,也有可能是ch2通道:

➜  mydemo git:(master) ✗ go run "/Users/liuyue/wodfan/work/mydemo/hello.go"  
ch1中取數據。。 200  
➜  mydemo git:(master) ✗ go run "/Users/liuyue/wodfan/work/mydemo/hello.go"  
ch1中取數據。。 200  
➜  mydemo git:(master) ✗ go run "/Users/liuyue/wodfan/work/mydemo/hello.go"  
ch2中取數據。。 200  
➜  mydemo git:(master) ✗

結語

綜上,Golang的通道其實就是將協程任務進行隔離,編寫併發邏輯時,關註通道即可,說白了,Golang的通道就是Python多進程通信中的管道,Golang雖然沒有顯性的多進程調用,但其協程調度底層就是多進程之間的通信,因為只有多進程才可能利用CPU的多核資源。


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

-Advertisement-
Play Games
更多相關文章
  • Web框架 Web框架可以簡單的理解為是基於互聯網的Web服務端>>>:socket服務端 1.WeB框架本質認識 1.我們可以這樣理解:我們所寫的Web框架其實就是一個socket服務端,而且用戶的瀏覽器就是一個socket客戶端。 2.本質上:瀏覽器是一個socket客戶端,伺服器是一個sock ...
  • HashMap源碼: 載入因數:loadFactory -- 預設 0.75f 初始容量大小: capacity 預設 16, 最大限制 1<<30 擴容: 當數組元素的數量 > 初始容量大小 * 載入因數,就會擴容. 會調用rehash方法將數組長度擴容到之前的兩倍.擴容的時候,會生成一個新的數組 ...
  • 1.數字類型 數字類型的數據可以相互的進行+-/*、也可以進行相互的比較(<>=) 1.1整型int age = 18 記錄年齡等整數 print(type(age))# int類型 int()方法可以將其他類型的數據轉換成int類型 1.1.2二、八、十六進位的相互轉換 1.十進位《 》二進位 # ...
  • 《笨辦法學Python3 》PDF高清版免費下載地址 ↑ ↑ ↑ ↑ ↑ ↑ ↑ 點擊即可下載 內容簡介 · · · · · · 本書是一本Python入門書籍,適合對電腦瞭解不多,沒有學過編程,但對編程感興趣的讀者學習使用。這本書以習題的方式引導讀者一步一步學習編程,從簡單的列印一直講到完整項目 ...
  • Java泛型02 5.自定義泛型 5.1自定義泛型類 基本語法: class 類名<T,R...>{//…表示可以有多個泛型 成員 } 註意細節: 普通成員可以使用泛型(屬性、方法) 使用泛型的數組不能初始化 靜態方法中不能使用類的泛型 泛型類的類型,是在創建類的對象時確定的(因為創建對象時,需要指 ...
  • ##HttpServletRequest request(請求) 所有的 和請求相關的操作,都用這對象來處理 當有請求來的時候 , request就被實例化 ##HttpServletResponse response(響應) 所有和響應相關的操作,都用這個對象來處理 當有請求來的時候 , resp ...
  • 問題描述: 前端使用Get請求並且使用請求體傳遞參數,後端使用@RequestBody註解封裝參數,這時會出現400的異常信息。 解決方法: 1、Get請求不要使用請求體,使用請求體的話用POST請求。(建議,這樣才是正常的規範寫法) 2、保留Get請求與請求體,後端也可以用對象來封裝請求體中的參數 ...
  • 首先上結構 mynode -> app5 -> urls.py & views.py | -> templates -> 5 -> upload.html | -> mynode -> urls.py | -> media 按照順序,先上app5/urls.py from django.urls i ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...