Golang并發編程及其高級特性

并發編程模型

線程模型:Go的Goroutine
  • Goroutine(M:N 模型)

    package mainimport ("fmt""runtime""sync""time"
    )func main() {// 查看當前機器的邏輯CPU核心數,決定Go運行時使用多少OS線程fmt.Println("CPU Cores:", runtime.NumCPU())// 啟動一個Goroutine:只需一個 `go` 關鍵字go func() {fmt.Println("I'm running in a goroutine!")}()// 啟動10萬個Goroutine輕而易舉var wg sync.WaitGroup // 用于等待Goroutine完成for i := 0; i < 100000; i++ {wg.Add(1)go func(taskId int) {defer wg.Done() // 任務完成時通知WaitGroup// 模擬一些工作,比如等待IOtime.Sleep(100 * time.Millisecond)fmt.Printf("Task %d executed.\n", taskId)}(i)}wg.Wait() // 等待所有Goroutine結束
    }
    
  • 極輕量

    • 內存開銷極小:初始棧大小僅2KB,并且可以按需動態擴縮容。創建100萬個Goroutine也只需要大約2GB內存(主要開銷是堆內存),而100萬個Java線程需要TB級內存。
    • 創建和銷毀開銷極低:由Go運行時在用戶空間管理,不需要系統調用,只是分配一點內存,速度極快(比Java線程快幾個數量級)。
  • M:N 調度模型:這是Go高并發的魔法核心。

    • Go運行時創建一個少量的OS線程(默認為CPU核心數,如4核機器就創建4個)。
    • 成千上萬的Goroutine被多路復用在這少量的OS線程上。
    • Go運行時自身實現了一個工作竊取(Work-Stealing) 的調度器,負責在OS線程上調度Goroutine。
  • 智能阻塞處理:當一個Goroutine執行阻塞操作(如I/O)時,Go調度器會立即感知到

    • 它會迅速將被阻塞的Goroutine從OS線程上移走。
    • 然后在該OS線程上調度另一個可運行的Goroutine繼續執行。
    • 這樣,OS線程永遠不會空閑,始終保持在忙碌狀態。阻塞操作完成后,相應的Goroutine會被重新放回隊列等待執行。
通信機制:Go的CSP模型:Channel通信
  • 語法和結構

    package mainimport ("fmt""time"
    )func producer(ch chan<- string) { // 參數:只寫Channelch <- "Data" // 1. 發送數據到Channel(通信)fmt.Println("Produced and sent data")
    }func consumer(ch <-chan string) { // 參數:只讀Channeldata := <-ch // 2. 從Channel接收數據(通信)// 一旦收到數據,說明“內存(數據)”的所有權從producer轉移給了consumerfmt.Println("Consumed:", data)
    }func main() {// 創建一個Channel(通信的管道),類型為stringmessageChannel := make(chan string)// 啟動生產者Goroutine和消費者Goroutine// 它們之間不共享內存,只共享一個Channel(用于通信)go producer(messageChannel)go consumer(messageChannel)// 給Goroutine一點時間執行time.Sleep(100 * time.Millisecond)// 更復雜的例子:帶緩沖的ChannelbufferedChannel := make(chan int, 2) // 緩沖大小為2bufferedChannel <- 1                 // 發送數據,不會阻塞,因為緩沖未滿bufferedChannel <- 2// bufferedChannel <- 3               // 這里會阻塞,因為緩沖已滿,直到有接收者拿走數據fmt.Println(<-bufferedChannel) // 接收數據fmt.Println(<-bufferedChannel)// 使用Range和Closego func() {for i := 0; i < 3; i++ {bufferedChannel <- i}close(bufferedChannel) // 發送者關閉Channel,表示沒有更多數據了}()// 接收者可以用for-range循環自動接收,直到Channel被關閉for num := range bufferedChannel {fmt.Println("Received:", num)}
    }
    
  • 核心:Goroutine 是被動的,它們通過 Channel 發送和接收數據來進行協作。通信同步了內存的訪問

  • Channel 的行為

    • 同步:無緩沖 Channel 的發送和接收操作會阻塞,直到另一邊準備好。這天然地同步了兩個 Goroutine 的執行節奏。
    • 所有權轉移:當數據通過 Channel 發送后,可以認為發送方“放棄”了數據的所有權,接收方“獲得”了它。這避免了雙方同時操作同一份數據。
  • 優點

    • 清晰易懂:數據流清晰可見。并發邏輯由 Channel 的連接方式定義,而不是由錯綜復雜的鎖保護區域定義。
    • 天生安全:從根本上避免了由于同時訪問共享變量而引發的數據競爭問題。
    • 簡化并發:開發者不再需要費心識別臨界區和手動管理鎖,大大降低了心智負擔和出錯概率。
  • Go 也提供了傳統的鎖sync.MutexChannel 并非萬能。Go 的理念是:

    • 使用 Channel 來傳遞數據、協調流程
    • 使用 Mutex 來保護小范圍的、簡單的狀態(例如,保護一個結構體內的幾個字段)。
同步原語:sync.Mutex、WaitGroup
  • sync.Mutex(互斥鎖)

    package mainimport ("fmt""sync"
    )type Counter struct {mu    sync.Mutex // 通常將Mutex嵌入到需要保護的數據結構中count int
    }func (c *Counter) Increment() {c.mu.Lock()         // 獲取鎖defer c.mu.Unlock() // 使用defer確保函數返回時一定會釋放鎖c.count++           // 臨界區
    }
    
    • 顯式操作:類似Java的Lock,需要手動調用Lock()Unlock()
    • defer是關鍵:Go社區強烈推薦使用defer mutex.Unlock()來確保鎖一定會被釋放,這比Java的try-finally模式更簡潔,不易出錯。
    • 不可重入:Go的Mutex是不可重入的。如果一個Goroutine已經持有一個鎖,再次嘗試獲取同一個鎖會導致死鎖
  • sync.WaitGroup(等待組)

    func main() {var wg sync.WaitGroup // 創建一個WaitGroupurls := []string{"url1", "url2", "url3"}for _, url := range urls {wg.Add(1) // 每啟動一個Goroutine,計數器+1go func(u string) {defer wg.Done() // Goroutine完成時,計數器-1(defer保證一定會執行)// 模擬抓取網頁fmt.Println("Fetching", u)}(url)}wg.Wait() // 阻塞,直到計數器歸零(所有Goroutine都調用了Done())fmt.Println("All goroutines finished.")
    }
    
    • WaitGroup更簡潔:它的API(Add, Done, Wait)專為等待Goroutine組而設計,意圖更明確,用法更簡單。
    • 無需線程池WaitGroup直接與輕量的Goroutine配合,而Java通常需要與笨重的線程池(ExecutorService)一起使用。
深度對比:Goroutine與Java線程的輕量級特性
  • 用戶態線程 vs. 內核態線程

    • Java線程1:1 模型的內核態線程,一個Java線程直接對應一個操作系統線程,由操作系統內核進行調度和管理。
    • GoroutineM:N 模型的用戶態線程,成千上萬個Goroutine被多路復用在少量操作系統線程上,在用戶空間進行調度和管理。
  • 內存開銷:Goroutine的內存效率比Java線程高出兩個數量級,這使得在普通硬件上運行數十萬甚至上百萬的并發任務成為可能。

  • 創建與銷毀:Goroutine的創建和銷毀開銷極低,這使得開發者可以采用更直觀的Goroutine模式,無需糾結于復雜的池化技術。

  • 調度:Go調度器的用戶態、協作式、工作竊取設計,使得它在高并發場景下的調度效率遠高于OS內核調度器。

  • 阻塞處理:Go在語言運行時層面完美處理了阻塞問題,而Java需要在應用層通過復雜的非阻塞I/O庫來規避此問題。

高級特性與元編程

泛型:Go的[T any](引入較晚,對比其應用場景)
  • 語法和結構

    // 1. 類型參數(Type Parameters)聲明:使用方括號 []
    //    `[T any]` 表示一個類型參數T,其約束為`any`(即沒有任何約束,可以是任何類型)
    func PrintSlice[T any](s []T) { // 泛型函數for _, v := range s {fmt.Println(v)}
    }// 2. 自定義約束(Constraints):使用接口定義類型集
    //    約束不僅可以要求方法,還可以要求底層類型(~int)或類型列表
    type Number interface {~int | ~int64 | ~float64 // 類型約束:只能是int、int64或float64(包括自定義衍生類型)
    }func Sum[T Number](s []T) T {var sum Tfor _, v := range s {sum += v}return sum
    }// 3. 泛型類型
    type MyStack[T any] struct {elements []T
    }func (s *MyStack[T]) Push(element T) {s.elements = append(s.elements, element)
    }func (s *MyStack[T]) Pop() T {element := s.elements[len(s.elements)-1]s.elements = s.elements[:len(s.elements)-1]return element
    }
    
  • 優點

    • 運行時類型安全:沒有類似Java的“原始類型”概念,無法繞過類型檢查。
    • 支持基本類型Sum([]int{1, 2, 3}) 可以直接工作,無裝箱開銷。
    • 更強大的約束:可以通過接口約束類型集(~int | ~float64),這是Java做不到的。
  • 缺點與限制(目前)

    • 語法略顯冗長[T any] 相比 <T> 更占空間,尤其是多個參數時:[K comparable, V any]
    • 生態系統仍在適應:標準庫和第三方庫對泛型的應用是漸進的,不像Java那樣無處不在。
反射:Java的Reflection vs Go的reflect
  • 語法和結構

    package mainimport ("fmt""reflect"
    )type Person struct {Name string `json:"name"` // 結構體標簽(Tag)Age  int    `json:"age"`
    }func (p Person) Greet() {fmt.Printf("Hello, my name is %s\n", p.Name)
    }func main() {// 1. 獲取Type和Value(反射的兩個核心入口)p := Person{Name: "Alice", Age: 30}t := reflect.TypeOf(p)   // 獲取類型信息 (reflect.Type)v := reflect.ValueOf(p)  // 獲取值信息 (reflect.Value)fmt.Println("Type:", t.Name()) // Output: Personfmt.Println("Kind:", t.Kind()) // Output: struct (Kind是底層分類)// 2. 檢查結構信息// - 檢查結構體字段for i := 0; i < t.NumField(); i++ {field := t.Field(i)tag := field.Tag.Get("json") // 獲取結構體標簽fmt.Printf("Field %d: Name=%s, Type=%v, JSON Tag='%s'\n",i, field.Name, field.Type, tag)}// - 檢查方法for i := 0; i < t.NumMethod(); i++ {method := t.Method(i)fmt.Printf("Method %d: %s\n", i, method.Name)}// 3. 動態操作// - 修改值(必須傳入指針,且值必須是“可設置的”(Settable))pValue := reflect.ValueOf(&p).Elem() // 獲取可尋址的Value (Elem()解引用指針)nameField := pValue.FieldByName("Name")if nameField.IsValid() && nameField.CanSet() {nameField.SetString("Bob") // 修改字段值}fmt.Println("Modified person:", p) // Output: {Bob 30}// - 調用方法greetMethod := v.MethodByName("Greet")if greetMethod.IsValid() {greetMethod.Call(nil) // 調用方法,無參數則傳nil// 輸出: Hello, my name is Alice (注意:v是基于原始p的Value,名字還是Alice)}// 4. 創建新實例var newPPtr interface{} = reflect.New(t).Interface() // reflect.New(t) 創建 *PersonnewP := newPPtr.(*Person)newP.Name = "Charlie"fmt.Println("Newly created person:", *newP) // Output: {Charlie 0}
    }
    
  • 顯式且謹慎:API設計清晰地分離了TypeValue,修改值需要滿足“可設置性”的條件,這是一種安全機制。

  • 功能側重不同

    • 強項:對結構體(Struct) 的解析能力極強,是encoding/json等標準庫的基石,結構體標簽(Tag) 是其特色功能。
    • 弱項:無法訪問未導出的成員(小寫開頭的字段/方法),這是Go反射一個非常重要的安全設計,它維護了包的封裝性。
  • Kind 的概念:這是Go反射的核心,Kind表示值的底層類型(如reflect.Struct, reflect.Slice, reflect.Int),而Type是具體的靜態類型,操作前常需要檢查Kind

  • 性能開銷:同樣有較大開銷,應避免在性能關鍵路徑中使用。

  • 類型安全:比Java稍好,但Call()等方法依然返回[]reflect.Value,需要手動處理。

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

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

相關文章

弧形導軌如何提升新能源汽車的能效和續航里程?

弧形導軌在新能源汽車中的應用主要集中在電池生產線和自動化裝配線等領域&#xff0c;通過提高生產效率和精度&#xff0c;間接提升新能源汽車的能效和續航里程。高精度裝配&#xff1a;在新能源汽車的電池生產線中&#xff0c;弧形導軌用于高精度的自動化裝配設備&#xff0c;…

考研擇校考慮因素和備考流程

考研擇校一、選擇專業二、選擇學校三、考研計劃安排一、選擇專業 1、了解自己的未來工作規劃&#xff08;這里肯定沒有啥規劃&#xff09;&#xff1b; 2、連接考研的相關幾個專業哪個好就業&#xff08;公務員和找工作&#xff09;&#xff1b; 3、知乎、小紅書、deepseek都可…

1.13 Memory Profiler Package - Unity Objects(unity對象頁簽)

1.Unity Objects(Unity對象頁簽)簡介 2.界面功能參數1.Unity Objects(Unity對象頁簽)簡介 Unity Objects用于快速定位unity對象內存占用的類型和具體實例a.查找內存占用最大的資源, 判斷這些資源是否可以壓縮或延遲加載b.查找重復加載的資源c.查看運行時創建但是沒有釋放的資源…

Android真機-安裝Reqable證書-抓SSL包

使用Reqable的自動安裝系統證書無法正常抓包&#xff0c;所以就有了這篇文章超簡單的安裝方式 - 記得確保手機已擁有root權限一、從Reqable導出公鑰證書無需使用OpenSSL 將 .pem 文件轉換為 .0 格式注意是 .0 格式的這個證書二、推送證書到手機adb root adb remount adb push 證…

[超表面論文快訊-242] PR-微波超四元數渦旋陣列洛書加權鎖定成像加密-江南大學王繼成、上海科技大學王雄團隊

欄目介紹&#xff1a; “論文快訊”欄目旨在精簡地分享一周內發表在高水平期刊上的Metasurface領域研究成果&#xff0c;幫助讀者及時了解領域前沿動態&#xff0c;如果對專欄的寫法或內容有什么建議歡迎留言&#xff0c;后續會陸續開啟其他專欄&#xff0c;敬請期待。 論文基…

案例研究:構建一個 Markdown 編輯器

引言&#xff1a;Markdown 編輯器案例在 Electron Node.js 開發中的研究價值與必要性 在 Electron 框架的實際項目應用中&#xff0c;構建一個 Markdown 編輯器是展示其強大能力的經典案例研究。它不僅僅是一個簡單的文本工具&#xff0c;更是開發者通過完整項目演示 Electron…

十四十五. 圖論

樹與圖的存儲 樹是一種特殊的圖,與圖的存儲方式相同。 對于無向圖中的邊ab,存儲兩條有向邊a->b, b->a。 因此我們可以只考慮有向圖的存儲。 (1) 鄰接矩陣:g[a][b] 存儲邊a->b (2) 鄰接表: // 對于每個點k,開一個單鏈表,存儲k所有可以走到的點。h[k]存儲這個單…

內存管理這一塊

文章目錄前言一、C/C內存分布二、C語言中動態內存管理方式三.C的內存管理方式new/delete操作內置類型new/delete操作自定義類型四.定位new總結前言 在一行一行的代碼之中&#xff0c;不同的數據存放的位置是有所不同的&#xff0c;正是因為這些數據的性質不同&#xff0c;所以…

linux 環境下Docker 安裝

Docker在線安裝 參考 &#xff1a;https://juejin.cn/book/6844733746462064654/section/6844733746545950734#heading-0 Ubuntu 環境下安裝 $ apt-get install apt-transport-https ca-certificates curl software-properties-common $ install -m 0755 -d /etc/apt/keyrin…

Netty從0到1系列之Netty啟動細節分析

文章目錄一、Netty服務器端啟動細節分析1.1 實現一個簡單的http服務器1.2 服務器端啟動細節分析1.3 創建與初始化 NioServerSocketChannel1.3.1 **通過反射工廠創建 Channel**&#xff1a;1.3.2 **初始化 Channel**1.4 注冊到 Boss EventLoopGroup1.4.1 **異步提交注冊任務**1.…

一個海康相機OCR的程序

這是一個極其復雜和龐大的??機器視覺檢測程序??&#xff0c;其核心特點是??多重冗余、條件判斷和流程分支??。它并非一個簡單的線性流程&#xff0c;而是一個為應對各種復雜工業場景&#xff08;如光照變化、產品位置偏移、識別難度高等&#xff09;而設計的??決策網…

深入解析:preload與prefetch的區別及最佳實踐

在前端性能優化領域&#xff0c;資源加載策略直接影響頁面的加載速度和用戶體驗。<link>標簽的preload和prefetch屬性是瀏覽器提供的兩種關鍵資源預加載機制&#xff0c;它們都能提前加載資源&#xff0c;但適用場景和行為邏輯卻大不相同。本文將從定義、觸發時機、優先級…

[論文閱讀] 人工智能 + 軟件工程(漏洞檢測)| 工業場景漏洞檢測新突破:CodeBERT跨領域泛化能力評估與AI-DO工具開發

工業場景漏洞檢測新突破&#xff1a;CodeBERT跨領域泛化能力評估與AI-DO工具開發 論文信息 論文原標題&#xff1a;Cross-Domain Evaluation of Transformer-Based Vulnerability Detection: Open-Source vs. Industrial Data引文格式&#xff08;APA&#xff09;&#xff1a;[…

【層面一】C#語言基礎和核心語法-01(類型系統/面向對象/異常處理)

文章目錄1 類型系統1.1 為什么需要類型&#xff1f;1.2 .NET 類型系統的兩大支柱&#xff1a;CTS 和 CLS1.3 最根本的分類&#xff1a;值類型 vs 引用類型1.4 內置類型 vs. 自定義類型1.5 類型轉換1.6 通用基類&#xff1a;System.Object2 面向對象編程2.1 類和對象2.2 接口和類…

Deepseek構建本地知識庫

一.本地部署Deepseek Ollama 介紹 目前市面上主流的&#xff0c;成本最低的部署本地大模型的方法就是通過 Ollama 了&#xff1a; Ollama 是一個開源的本地大語言模型運行框架&#xff0c;專為在本地機器上便捷部署和運行大型語言模型&#xff08;LLM&#xff09;而設計。 核心…

idea自動編譯,idea不重啟項目,加載修改的內容

idea自動編譯&#xff0c;idea不重啟項目&#xff0c;加載修改的內容

幸運盒項目—測試報告

幸運盒測試報告 目錄幸運盒測試報告一. 概要二. 測試環境三. 測試用例腦圖四. 測試用例1. 功能測試1. 注冊功能2. 密碼登錄功能3. 驗證碼登錄功能4. 注冊用戶功能5. 創建獎品功能6. 新建抽獎活動功能8. 獎品列表9. 活動列表2. 界面測試1. 注冊界面2. 密碼登錄界面3. 驗證碼登錄…

Estimator and Confidence interval

Coefficient of determination and sample correlation coefficient R2SSRSSTR^2 \frac{SSR}{SST}R2SSTSSR? SSR∑i1n((yi^?yˉ)2)SSR\sum_{i1}^n((\hat{y_{i}}-\bar{y})^2)SSR∑i1n?((yi?^??yˉ?)2) SST∑i1n((yi?yˉ)2)SST\sum_{i1}^n((y_{i}-\bar{y})^2)SST∑i1n?…

【網絡編程】TCP 服務器并發編程:多進程、線程池與守護進程實踐

半桔&#xff1a;個人主頁&#x1f525; 個人專欄: 《Linux手冊》《手撕面試算法》《網絡編程》&#x1f516;很多人在喧囂聲中登場&#xff0c;也有少數人在靜默中退出。 -張方宇- 文章目錄前言套接字接口TCP服務器TCP 多進程TCP 線程池重寫Task任務放函數對象客戶端重連進程…

還停留在批處理時代嗎?增量計算架構詳解

在當今的數字化環境中&#xff0c;企業不再只是一味地囤積數據——他們癡迷于盡快把數據轉化為可付諸行動的洞察。真正的優勢來自于實時發現變化并立即做出反應&#xff0c;無論是調整推薦策略還是規避危機。 十年前&#xff0c;硬件與平臺技術的進步讓我們能夠從容應對海量數…