【Hello Go】Go語言并發編程

并發編程

    • 概述
      • 基本概念
      • go語言的并發優勢
    • goroutine
      • goroutine是什么
      • 創建goroutine
      • 如果主goroutine退出
      • runtime包
        • Gosched
        • Goexit
        • GOMAXPROCS
    • channel
      • 無緩沖的channel
      • 有緩沖的channel
      • range和close
      • 單向channel
    • 定時器
      • Timer
      • Ticker
    • Select
      • 超時

概述

基本概念

并行和并發概念

  • 并行 :在同一時刻 有多條指令在多個編譯器上
  • 并發 :在同一時刻 只能有一條指令執行 但是多個進程指令被快速的輪換執行 使得在宏觀上有多個進程被同時執行的效果

如果我們把它具象化成現實中的概念

  • 并行就是同一時刻 兩個隊列使用兩臺咖啡機
  • 并發就是同一時刻 兩個隊列使用一臺咖啡機

go語言的并發優勢

第一 Go語言在語言層面上天然支持并發 (不像某個語言 23版本才勉強上線)

第二 并發編程的內存管理都是十分復雜的 而Go語言支持GC即 垃圾回收機制


Go語言為了支持并發編程而內置的上層API是基于 CSP (順序通信進程) 模型 這就意味著顯示鎖都是可以避免的 而Go語言通過相冊安全的通道發送和接受數據以實現同步 這大大簡化了并發程序的編寫

一般情況下 一個普通的桌面計算機系統跑十幾二十個線程就會有點負載了 但是同樣的這臺計算機卻能輕松的讓成百上千甚至過萬個 goroutine進行資源競爭

goroutine

goroutine是什么

goroutine是Go并發設計的核心 說到底 其實它是協程 但是它比線程更小 十幾個goroutine在底層的體現可能是幾個線程

Go語言內部幫你實現了幫你實現了這些goroutine之間的內存共享 執行它只需要極少的棧內存 大概(4~5kb) 正因為如此 可以同時運行成千上萬個goroutine任務

goroutine比thread更高效 更簡單 更輕便

創建goroutine

只需要在函數調用之前添加go關鍵字 就可以創建并發執行單元 開發人員無需了解任何細節 調度器會自動將其安排到合適的系統線程上執行

在并發編程里 我們通常想將一個過程切分成幾塊 并且然后讓每個goroutine負責它的一部分 當一個程序運行時 它的主函數即在一個單獨的goroutine中執行 我們把它叫做 main goroutine

而新的goroutine使用go語句來創建

代碼演示如下

func testnewgor() {for i := 0; i < 5; i++ {fmt.Println("new goroutine say :", i)time.Sleep(time.Second)}
}func main() {go testnewgor()for i := 0; i < 5; i++ {fmt.Println("main goroutine say :", i)time.Sleep(time.Second)}
}

運行這段代碼之后我們會發現主協程 新協程會同時打印語句

如果主goroutine退出

如果說主goroutine推出了 并不會有類似linux中孤兒進程的概念 其他的goroutine也會立即退出

runtime包

Gosched

runtime.gosched() 用于讓出CPU時間片 讓出當前協程的執行權限 調度器會安排其他等待的任務執行 并在下次的某個時刻從該位置開始恢復執行

這就像接力賽一樣 A跑了一段時間遇到代碼runtime.gosched() 之后將接力棒交給B 之后B跑了一段時間遇到代碼runtime.gosched()之后將接力棒交給A

下面是示例代碼

func main() {go func() {for i := 0; i < 5; i++ {runtime.Gosched()fmt.Println("world")}}()// main gorotinuefor i := 0; i < 5; i++ {fmt.Println("hello")runtime.Gosched()}// 最后結果為  hello  world  hello world ... ...}
Goexit

調用Goexit函數將會立即終止當前goroutine執行 調度器會確保所有的defer調用被執行

下面是示例代碼演示

	go func() {defer fmt.Println("this is A")runtime.Goexit()defer fmt.Println("this is B")fmt.Println("this is C")}()  // 只會打印 this is A   因為后面的延時調用語句還沒來得及執行協程就退出了 // 不讓主協程退出  觀察其他攜程的掩飾效果for {}
GOMAXPROCS

GOMAXPROCS在Go語言中是一個環境變量 它表示可以Go語言可以并發的最大核心數

如果是 runtime.GOMAXPROCS(size int) 函數 我們有兩種用法

  • 第一種是將參數設置0 此時會返回我們當前的最大核心數
  • 第二種是將參數設置為其他正整數 此時核心會變為我們設置的值

channel

它和map類似 channel也是一個對于make創建的底層數據結構的引用

當我們復制了一個channel用于函數傳參時 我們只是拷貝了一個channel引用 因此調用者和被調用者將使用同一個channel對象 和其他的引用類型一樣 channel的零值也是nil

定義一個channel時 我們也需要定義發送到chanel值的類型 channel可以使用內置的make()函數來實現

make(chan Type) //等價于make(chan Type, 0)
make(chan Type, capacity)

當capacity等于0的時候 是無緩沖阻塞式讀寫的

當capacity大于0的時候 是有緩沖非阻塞的 直到寫入的數據大于capacity才會阻塞住

channel通過操作符<-來接收和發送數據 發送和接收數據語法如下

channel <- value // 發送value到channel 
<- channel // 接受并且丟棄所有數據 
x := <- channel  // 從channel接受數據 并且賦值給x
x , ok := <- channel  // 功能同上 不過增加了一個bool類型的數據來檢查通道是否關閉或者是否為空 

在默認情況下 channel接受和發送數據都是阻塞的 除非另一端已經準備好了 這就讓goroutine的同步變得簡單 不需要顯示的lock了

	c := make(chan int)go func() {fmt.Println("子協程正在運行")defer fmt.Println("子協程已結束")c <- 666}()fmt.Println("主協程正在運行")x := <-ctime.Sleep(time.Second)fmt.Println("子協程發送的值為", x)

無緩沖的channel

無緩沖的通道(unbuffered channel)是指在接收前沒有能力保存任何值的通道

這種類型的通道要求發送 goroutine 和接收 goroutine 同時準備好,才能完成發送和接收操作。如果兩個goroutine沒有同時準備好,通道會導致先執行發送或接收操作的 goroutine 阻塞等待。

這種對通道進行發送和接收的交互行為本身就是同步的。其中任意一個操作都無法離開另一個操作單獨存在。

我們上面的代碼就是一個無緩沖channel 這里為了方便大家理解再發一遍

	c := make(chan int)go func() {fmt.Println("子協程正在運行")defer fmt.Println("子協程已結束")c <- 666}()fmt.Println("主協程正在運行")x := <-ctime.Sleep(time.Second)fmt.Println("子協程發送的值為", x)

有緩沖的channel

有緩沖的channel創建方式如下

make(chan Type, capacity)

此時它阻塞的方式也發生了變化

  • 如果緩沖區滿了并且還在寫數據此時會寫入阻塞
  • 如果緩沖區空了并且還在讀數據此時會讀取阻塞

range和close

我們可以通過close來關閉一個channel

close (chan)
  • channel 不像文件一樣需要經常去關閉 只有當你確實沒有任何發送數據了 或者要結束range循環才關閉
  • 關閉之后無法再發送任何的數據 發數據會引發panic異常
  • 關閉后可以接受數據
  • 接受數據會阻塞住

此外我們還可以通過range迭代來獲取數據 一旦管道關閉 range循環就會結束

單向channel

默認情況下,通道是雙向的,也就是,既可以往里面發送數據也可以同里面接收數據

但是,我們經常見一個通道作為參數進行傳遞而值希望對方是單向使用的,要么只讓它發送數據,要么只讓它接收數據,這時候我們可以指定通道的方向

單向channel變量的聲明非常簡單,如下:

var ch1 chan int       // ch1是一個雙向的管道
var ch2 chan<- float64 // ch2只能往里寫入float64數據
var ch3 <-chan int     // ch3只能用于接受int類型的數據
  • chan<- 表示數據進入管道,要把數據寫進管道,對于調用者就是輸出。
  • <-chan 表示數據從管道出來,對于調用者就是得到管道的數據,當然就是輸入

我們可以將channel隱式的轉化為單向隊列只收或者只發 不能將單向的channel轉化為普通channel

轉換的語法如下

c := make(chan int, 3)
var send chan<- int = c // send-only
var recv <-chan int = c // receive-only

下面是完整的使用代碼

func recv(out <-chan int) {for x := range out {fmt.Println(x)}
}func send(in chan<- int) {for i := 0; i < 5; i++ {in <- i * 100}close(in)
}func main() {c := make(chan int, 3)go send(c)recv(c)time.Sleep(3 * time.Second)
}

定時器

Timer

timer是一個定時器 代表未來的一個單一事件 你可以告訴timer這個時間要等待的時間 它會提供一個channel 在將來的那個時間 channel提供了一個時間值

下面是示例代碼

func main() {// 創建定時器 兩秒后定時器就會像自己的c字節發送一個time.TIME類似的元素值timer1 := time.NewTimer(2 * time.Second)t1 := time.Now() // 當前時間fmt.Printf("t1 : %v\n", t1)t2 := <-timer1.Cfmt.Println("t2:", t2)
}

我們在創建定時器之后的兩秒鐘會收到一個時間 之后我們可以將該時間和現在的時間對比一下 我們發現正好相差了兩秒

Ticker

Ticker是一個定時觸發的計時器 它會以一個間隔往channel中發送一個事件 而channel的接收者可以以固定的時間間隔從channel中讀取事件

下面是示例代碼

func main() {// 創建一個定時器 每隔一秒像channel中發送一個事件ticker := time.NewTicker(time.Second * 1)i := 0go func() {for i = 0; i < 5; i++ {<-ticker.Cprintln("goroutine say : ", i)}// 最后關閉tickerticker.Stop()}()for {}
}

Select

Go語言提供了一個關鍵字select 通過select可以監聽channel上的數據流動

select的用法和switch十分相似 由select選擇一個新的模塊 之后每個選擇條件由case語句來描述

此外select語句對比switch語句來說有諸多的限制 其中最大的一條限制就是每一條語句里面必須有一個IO操作 大致結構如下

	select {case <-chan1: // 如果chan1成功讀取到數據 則執行該操作// ....case chan2 <- 1: // 如果chan2成被寫入數據 則執行官該操作default:}

在一個select語句中 Go語言會按照順序評估每個發送和接受的語句 如果說有任意條語句可以執行 那么就從這些可執行的語句中任選一條來使用

如果說所有的通道都被阻塞了 那么此時有兩種情況

  • 如果給出了default語句 那么就會執行default語句 并且程序會從select語句后恢復
  • 如果沒有default語句 那么default語句將會被阻塞 直到一個case可用
func fib(c, q chan int) {x, y := 1, 1for {select {case c <- x: // 如果c輸出了數據x, y = y, x+ycase <-q: // 如果q被寫入了數據fmt.Println("quit")return}}
}func main() {c := make(chan int)quit := make(chan int)go func() {for i := 0; i < 6; i++ {fmt.Println(<-c)}quit <- 0}()fib(c, quit)
}

值得注意的是select中 case c <- x: 的含義 它的意思是 c可以寫入數據的時候執行 那么c什么時候可以寫入數據呢? 當然是有人要接受數據的時候

所以說我們的 fmt.Println(<-c) 語句有兩個作用

  1. 接受數據并打印
  2. 讓c可以寫入數據

運行結果如下

在這里插入圖片描述

超時

有時候我們會遇到goroutine阻塞的情況 那么我們如何避免整個程序陷入阻塞呢 我們可以通過設置超時來實現

語法如下

func main() {c := make(chan int)q := make(chan int)o := make(chan bool)go func() {select {case c <- 0: // 當c可以寫入數據的時候println("可寫入")case <-q: // 當q可以輸出數據的時候println("可輸出")case <-time.After(5 * time.Second):println("超時")o <- falseprintln("我運行完畢了")break}}()<-o
}

這段代碼的最終結果就是打印一個超時之后結束進程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/164038.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164038.shtml
英文地址,請注明出處:http://en.pswp.cn/news/164038.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CVE-2023-6099:優卡特臉愛云一臉通智慧管理平臺SystemMng.ashx接口未授權漏洞復現

文章目錄 優卡特臉愛云一臉通智慧管理平臺未授權SystemMng.ashx接口漏洞復現&#xff08;CVE-2023-6099&#xff09; [附POC]0x01 前言0x02 漏洞描述0x03 影響版本0x04 漏洞環境0x05 漏洞復現1.訪問漏洞環境2.構造POC3.復現 0x06 修復建議 優卡特臉愛云一臉通智慧管理平臺未授權…

mysql字符串轉為數字的三種方法、字符串轉日期

隱式轉換 在MySQL中&#xff0c;使用0運算符可以將一個非數字的值隱式地轉換為數字。這在進行數學運算或比較操作時非常有用。 需要注意的是&#xff0c;在使用0進行隱式轉換時&#xff0c;MySQL會盡可能將字符串轉換為數字。如果字符串不能轉換為數字&#xff0c;則會返回0。…

【解決】HDFS JournalNode啟動慢問題排查

文章目錄 一. 問題描述二. 問題分析1. 排查機器性能2. DNS的問題 三. 問題解決 一句話&#xff1a;因為dns的問題導致journalnode啟動時很慢&#xff0c;通過修復dns對0.0.0.0域名解析&#xff0c;修復此問題。 一. 問題描述 從journalnode啟動到服務可用&#xff0c;完成RPC…

使用Python將圖片轉換為PDF

將圖片轉為 PDF 的主要原因之一是為了方便共享和傳輸。此外&#xff0c;將多張圖片合并成一個 PDF 文件還可以簡化文件管理。之前文章詳細介紹過如何使用第三方庫Spire.PDF for Python將PDF文件轉為圖片&#xff0c;那么本文介紹使用同樣工具在Python中實現圖片轉PDF文件的功能…

【OpenCV+OCR】計算機視覺:識別圖像驗證碼中指定顏色文字

文章目錄 1. 寫在前面2. 讀取驗證碼圖像3. 生成顏色掩碼4. 生成黑白結果圖5. OCR文字識別6. 測試結果 【作者主頁】&#xff1a;吳秋霖 【作者介紹】&#xff1a;Python領域優質創作者、阿里云博客專家、華為云享專家。長期致力于Python與爬蟲領域研究與開發工作&#xff01; 【…

Spring Security(安全框架,必須登錄成功才能訪問指定資源)

一、背景知識 1、Spring Security 是一個能夠為基于Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean&#xff0c;充分利用了Spring IoC&#xff0c;DI&#xff08;IOC: 控制反轉Inversion of Control ,DI:D…

24路電磁鎖控板的特點和主要參數

智能快遞柜、智能生鮮柜、電子存儲柜、超市寄存柜、智能送餐柜、電子更衣柜、檔案柜等物聯網終端設備&#xff0c;都是采用電磁鎖控制&#xff0c;這種電磁鎖控制板俗稱鎖控板。鎖控板可以遠程控制儲物柜的開關以及遠程監控并提供鎖的反饋信號。沐渥開發的24路電磁鎖控板可以控…

AI:87-基于深度學習的街景圖像地理位置識別

?? 本文選自專欄:人工智能領域200例教程專欄 從基礎到實踐,深入學習。無論你是初學者還是經驗豐富的老手,對于本專欄案例和項目實踐都有參考學習意義。 ??? 每一個案例都附帶有在本地跑過的代碼,詳細講解供大家學習,希望可以幫到大家。歡迎訂閱支持,正在不斷更新中,…

OpenAI 曾收到 AI 重大突破警告;半獨立的 OpenAI 比與微軟合并更好丨 RTE 開發者日報 Vol.91

開發者朋友們大家好&#xff1a; 這里是 「RTE 開發者日報」 &#xff0c;每天和大家一起看新聞、聊八卦。我們的社區編輯團隊會整理分享 RTE &#xff08;Real Time Engagement&#xff09; 領域內「有話題的 新聞 」、「有態度的 觀點 」、「有意思的 數據 」、「有思考的 文…

ubuntu下docker環境使用GPU配置

本文主要講述整個命令流程&#xff0c;具體講解請看官網nvidia-容器工具包和一篇總結得很詳細的博文docker使用GPU總結 docker的版本必須安裝19.0版本以上的&#xff0c;這里也只講19.0版本以上的使用方法 首先設置一下網絡信息 curl -fsSL https://nvidia.github.io/libnvi…

LeetCode131. Palindrome Partitioning

文章目錄 一、題目二、題解 一、題目 Given a string s, partition s such that every substring of the partition is a palindrome . Return all possible palindrome partitioning of s. Example 1: Input: s “aab” Output: [[“a”,“a”,“b”],[“aa”,“b”]] Exa…

工具【1、計算時間差2、獲取當天時間前后七天時間3、根據當前數據的位置,在數組中插入數據4、數組中,某個屬性相同的數據放在一塊,如把某個日期相同的相連一起】

生成UUID /*** 唯一的隨機字符串&#xff0c;用來區分每條數據* returns {string}*/ export function getUid() {return xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.replace(/[xy]/g, (c) > {const r (Math.random() * 16) | 0;const v c x ? r : (r & 0x3) | 0x8;retu…

【圖文詳解】SiamFC++與圖注意力的強強聯合:單目標追蹤系統

1.研究背景與意義 隨著計算機視覺技術的不斷發展&#xff0c;單目標追蹤&#xff08;Single Object Tracking, SOT&#xff09;作為計算機視覺領域的一個重要研究方向&#xff0c;已經在許多實際應用中得到了廣泛的應用。單目標追蹤系統可以通過分析視頻序列中的目標運動&…

服務器流量包扣減規則

服務器買的流量包,一般指的是上行帶寬,下行通常是不限的 上行和下行是針對服務器而言的 客戶端上傳文件給服務器,對服務器而言它是在下載,所以對服務器而言他是用的下行帶寬(下行流量) 客戶端從服務器下載文件,對服務器而言它是在上傳,所以對服務器而言他是用的上行帶寬(上行…

大數據量條件SQL查詢內存處理方案以及數據過濾算法優化

MySQL是一個廣泛使用的關系型數據庫管理系統。通過SQL語言進行數據操作和查詢&#xff0c;還支持多用戶、多線程和分布式操作等功能。 在實際使用中&#xff0c;我們會遇到各種查詢條件&#xff0c;如字段名、表名、邏輯運算符、比較運算符、函數等。其中&#xff0c;有些查詢…

淺析智慧社區建設趨勢及AI大數據監管平臺方案設計

一、背景與需求 伴隨著社會與經濟的發展&#xff0c;人們對生活質量的要求越來越高&#xff0c;與此同時&#xff0c;新興技術的進步也促進了智慧社區市場的逐步成熟。智慧社區是社區管理的一種新理念&#xff0c;是新形勢下城市與社會管理的一種創新模式。 在上海、杭州、深…

在.bashrc文件修改環境變量的做法

作者&#xff1a;朱金燦 來源&#xff1a;clever101的專欄 為什么大多數人學不會人工智能編程&#xff1f;>>> ~/.bashrc文件是linux下保存環境變量的系統文件。原以為使用sed命令修改.bashrc文件&#xff0c;實際上不行&#xff0c;需要使用echo命令。具體示例如下…

02-詳細介紹Java8新特性方法引用,構造引用,數組引用

方法/構造/數組引用 方法引用 當要傳遞給Lambda體的操作已經有實現的方法時就可以使用方法引用,方法引用和構造器引用就是為了簡化Lambda表達式 方法引用可以看做是Lambda表達式深層次的表達,方法引用本質還是Lambda表達式所以也是函數式接口的一個實例通過方法的名字來指向…

小紅書關鍵詞搜索商品列表API接口(分類ID搜索商品數據接口,商品詳情接口)演示案例

通過關鍵詞搜索商品API接口&#xff0c;電商平臺可以為消費者提供一個簡單、快捷的商品搜索功能。用戶只需輸入關鍵詞&#xff0c;就可以得到與該關鍵詞相關的商品列表。關鍵詞搜索商品API接口還可以提供給第三方開發者一個便捷的商品搜索服務。開發者可以利用該接口&#xff0…

Mac安裝配置typescript及在VSCode上運行ts

一、Mac上安裝typescript sudo npm install -g typescript 測試一下&#xff1a;出現Version則證明安裝成功 tsc -v 二、在VSCode上運行 新建一個xxx.ts文件&#xff0c;測試能否運行 console.log("helloworld") 運行報錯&#xff1a;ts-node: command not…