Golang的Goroutine(協程)與runtime

目錄

Runtime 包概述

Runtime 包常用函數

1. GOMAXPROCS

2. Caller 和 Callers

3. BlockProfile 和 Stack

理解Golang的Goroutine

Goroutine的基本概念

特點:

Goroutine的創建與啟動

示例代碼

解釋

Goroutine的調度

Gosched的作用

示例代碼

輸出

解釋

Goroutine的性能優化

示例代碼

解釋

實際應用中的使用場景

總結

Runtime與Routine的關系

補充(代碼)


Runtime 包概述

runtime 包提供了與 Go 運行時環境交互的功能,包括 goroutine 調度、內存管理、堆棧操作等。通過 runtime 包,我們可以更好地控制程序的運行行為。

Runtime 包常用函數

1. GOMAXPROCS

設置可以并行執行的最大 CPU 數,并返回之前的設置值。

fmt.Println(runtime.GOMAXPROCS(2))

2. Caller 和 Callers

runtime.Caller?和?runtime.Callers?用于獲取調用棧信息。

pc, file, line, ok := runtime.Caller(1)  
if !ok {  fmt.Println("runtime.Caller() failed")  return  
}  
fmt.Printf("PC: %v, File: %s, Line: %d\n", pc, file, line)  

3. BlockProfile 和 Stack

runtime.BlockProfile()?用于獲取阻塞概述,而?runtime.Stack()?則可以打印當前 goroutine 或所有 goroutine 的堆棧跟蹤信息。


理解Golang的Goroutine

Goroutine是Golang語言中的輕量級線程,能夠在單一操作系統線程上運行多個Goroutine,從而提高并發編程的效率。本文將通過實際的代碼示例,詳細講解Goroutine的創建、調度、性能優化以及在實際應用中的使用場景。


    Goroutine的基本概念

    Goroutine 是Golang語言中用于并發編程的基本單位。與傳統的線程(Thread)相比,Goroutine的調度和切換成本更低,因為它們是基于Golang的協作式調度模型設計的。每個Goroutine的棧大小默認為2KB,但隨著其生命周期的變化,棧大小會自動擴大以適應需求。

    特點:

    • 輕量級:創建和銷毀Goroutine的開銷非常低。
    • 高并發:Golang可以輕松支持數萬甚至更多的Goroutine同時運行。
    • ** Channels**:Goroutine之間通過Channel進行通信,避免了共享內存帶來的競態條件問題。

    Goroutine的創建與啟動

    Goroutine的創建非常簡單,只需要在函數調用前加上go關鍵字即可啟動一個新的Goroutine。

    示例代碼
    package mainimport ("fmt""runtime"
    )func print() {fmt.Println("這是一個新的Goroutine。")
    }func main() {go print()  // 啟動一個新的Goroutinefmt.Println("主Goroutine結束。")  // 主Goroutine繼續執行
    }
    

    解釋

    • go print():啟動一個新的Goroutine執行print函數。
    • 主Goroutine繼續執行fmt.Println("主Goroutine結束。")
    • 需要注意的是,如果主Goroutine執行完畢,程序可能會直接退出,可能無法看到子Goroutine的輸出。為了確保子Goroutine有足夠的時間完成,可以通過time.Sleep()或者其他同步機制來控制。

    Goroutine的調度

    Goroutine的調度是由Golang的運行時環境負責的。與線程不同,Goroutine采用的是非搶占式調度,即只有在當前Goroutine主動讓出CPU(例如調用runtime.Gosched())時,其他Goroutine才能獲取執行機會。

    Gosched的作用

    runtime.Gosched()用于讓出當前Goroutine的執行權限,允許其他Goroutine運行。

    示例代碼
    package mainimport ("fmt""runtime"
    )func main() {go func() {for i := 1; i <= 5; i++ {runtime.Gosched()fmt.Println(i)}}()fmt.Println("主Goroutine結束。")
    }
    
    輸出
    主Goroutine結束。
    1
    2
    3
    4
    5
    

    解釋

    • 子Goroutine在循環中調用runtime.Gosched(),主動讓出CPU時間片。
    • 主Goroutine在子Goroutine開始后立即執行并輸出“主Goroutine結束。”。

    Goroutine的性能優化

    Goroutine的性能優化主要體現在以下幾個方面:

    1. 設置GOMAXPROCS:通過runtime.GOMAXPROCS(n)設置可以并行執行的最大CPU核數。合理設置這個值可以提高程序的并行度。

      • 示例:runtime.GOMAXPROCS(2),表示最多同時使用2個CPU核心。
    2. 避免競態條件:多個Goroutine訪問共享變量時,可能會出現競態條件。可以通過Channel或sync包中的同步原語(如Mutex、RWMutex)來避免。

    3. 合理分配任務:在高并發場景下,合理分配任務可以提高程序的執行效率。

    示例代碼
    package mainimport ("fmt""math""runtime""sync""time"
    )func findPrime(num int, inChan chan int, primeChan chan int, exitChan chan bool) {var flag boolfor {v, ok := <-inChanif !ok {break}flag = truefor i := 2; i < int(math.Sqrt(float64(v))); i++ {if v % i == 0 {flag = falsebreak}}if flag {primeChan <- v}}exitChan <- true
    }func main() {num := 10000000  // 查找小于num的所有質數start := time.Now()inChan := make(chan int, num)primeChan := make(chan int, num)exitChan := make(chan bool, 4)  // 假設使用4個Goroutinego func() {for i := 2; i < num; i++ {inChan <- i}close(inChan)}()for i := 0; i < 4; i++ {go findPrime(num, inChan, primeChan, exitChan)}go func() {for i := 0; i < 4; i++ {<-exitChan}close(primeChan)}()count := 0for v := range primeChan {count++}fmt.Printf("找到%d個質數,用時:%v\n", count, time.Since(start))
    }
    

    解釋

    • Channel通信:通過Channel傳遞數據,避免了共享變量帶來的競態條件。
    • 并行計算:通過啟動多個Goroutine并行計算質數,提高了程序的執行效率。
    • 同步機制:通過exitChan等待所有Goroutine完成任務。

    實際應用中的使用場景

    1. 并發請求處理:在Web服務器中,通過Goroutine并行處理多個HTTP請求,提高吞吐量。
    2. 數據處理:在數據處理任務中,通過Goroutine并行處理不同的數據片段,提高處理速度。
    3. I/O密集型任務:在I/O密集型任務(如文件讀寫、網絡通信)中,Goroutine可以通過非阻塞的方式提高資源利用率。

    總結

    Goroutine是Golang語言中的并行編程核心,具有輕量級、高效和靈活的特點。通過合理利用Goroutine,可以顯著提高程序的性能和響應速度。在實際應用中,需要注意避免競態條件,合理分配任務,并通過Channel等方式實現Goroutine之間的安全通信。


    Runtime與Routine的關系

    在Go語言中,runtimeroutine是兩個密切相關但又有明確區別的概念。runtime主要指的是Go語言的運行時環境,它為Go程序的執行提供了必要的支持,如內存管理、Goroutine調度、錯誤處理等。而routine在Go語言中特指Goroutine,即輕量級的線程。

    具體來說:

    1. Runtime

      • 定義runtime是一個Go語言標準庫中的包,提供了與程序運行時環境相關的功能。
      • 功能
        • Goroutine調度:管理Goroutine的創建、執行和調度。
        • 內存管理:負責內存的分配和回收,包括垃圾回收機制。
        • 錯誤處理:提供了一些與錯誤處理相關的函數,如runtime.Error
        • 與操作系統交互:處理程序運行時的環境,如線程數、CPU核數等。
        • 性能監控:提供了一些與性能監控相關的函數,如內存使用統計。
      • 常用函數
        • Gosched():讓出當前Goroutine的執行權限,允許其他Goroutine運行。
        • GOMAXPROCS():設置或獲取可以并行執行用戶級處理的最大CPU核數。
        • NumCPU():返回當前可用的CPU核數。
        • Goexit():使當前Goroutine退出,然后調度器會打印當前的棧跟蹤信息。
    2. Routine(Goroutine)

      • 定義:Goroutine是Go語言中的輕量級線性,它由Go的運行時環境管理。
      • 特點
        • 輕量級:創建和銷毀Goroutine的開銷比傳統線程低。
        • 高并發:可以輕松支持數萬甚至更多的Goroutine同時運行。
        • 非阻塞式調度:基于協作式調度,主動讓出CPU時間片。
      • 創建方式:通過go關鍵字啟動一個新的Goroutine。
      • 常見用途
        • 并發執行任務:將一個函數或方法轉換為異步執行,不阻塞主Goroutine。
        • 處理I/O密集型任務:在I/O操作中,釋放資源讓其他Goroutine使用。
        • 并行計算:在多核CPU上并行執行任務以提高計算效率。
    3. 兩者的關系

      • 調度管理runtime包負責管理Goroutine的調度,包括如何分配時間片、如何在多個CPU核間分配Goroutine等。
      • 資源管理:通過runtime包,可以控制Goroutine的數量和并行度,優化程序性能。
      • 協作式調度:Goroutine通過runtime.Gosched()主動讓出CPU時間片,協作式調度依賴于Goroutine自身的配合,而不是操作系統強制切換。

    補充(代碼)

    package mainimport ("fmt""math""runtime""time"
    )func PrintCallerInfo() {//返回調用棧的信息(文件名、行號等)。//參數:skip表示從調用Caller開始往回數多少層的調用棧信息。// 	skip = 0: 返回調用runtime.Caller的函數(即直接包含runtime.Caller(0)調用的那行代碼)的文件名、行號等信息。// skip = 1: 返回調用runtime.Caller所在函數的直接調用者的文件名和行號。換句話說,就是調用了包含runtime.Caller(1)的函數的地方的調用棧信息。// skip = 2: 返回上一步調用者的調用者信息,依此類推。每增加1,就向上追溯一個調用棧幀。// 以此類推,隨著skip值的增加,可以逐級向上追溯到更早的調用棧信息。如果指定的層級超出了實際存在的調用棧層數,則ok將為false,其他返回值(pc, file, line)將沒有意義或為零值。pc, file, line, ok := runtime.Caller(1)if !ok {fmt.Println("runtime.Caller() failed")return}fmt.Printf("PC: %v, File: %s, Line: %d\n", pc, file, line)
    }func main() {// 設置可以并行執行的最大CPU數,并返回之前的設置值;控制了Go程序可以同時使用的操作系統線程數量。fmt.Println(runtime.GOMAXPROCS(2))// 啟動一個協程go func(x int) {//time.Sleep(1 * time.Second)fmt.Println("Goroutine", x)}(1)// 終止go func(x int) {defer fmt.Println("Goroutine", x)//time.Sleep(1 * time.Second)runtime.Goexit() // 當前goroutine 退出fmt.Println("This won't print")}(2)// 當前程序中活躍的gotoutines數量fmt.Println("NumGoroutine:", runtime.NumGoroutine())// 讓出當前goroutine的處理器,允許其他等待的goroutines運行;// 這是一個提示,而不是強制性的上下文呢切換go func() {for i := 1; i <= 5; i++ {runtime.Gosched()fmt.Println(i)}}()go func(x int) {//time.Sleep(1 * time.Second)fmt.Println("Goroutine", x)}(3)go func(x int) {//time.Sleep(1 * time.Second)fmt.Println("Goroutine", x)}(4)// 其它//runtime.BlockProfile() //獲取當前的阻塞概要信息(block profile),它用于分析goroutine之間的同步問題,如鎖競爭、通道阻塞等。//runtime.Caller() //返回調用棧的信息(文件名、行號等)。//runtime.Callers() //類似Caller,但返回的是多個調用棧幀的信息。//runtime.GC() //:手動觸發垃圾回收。通常不需要手動調用,Go的垃圾收集器會自動管理內存。fmt.Println(runtime.GOROOT()) // 返回Go的安裝目錄//runtime.KeepAlive(x any) //確保對象在其作用域結束前不會被垃圾回收。//runtime.Stack() //將當前goroutine或所有goroutines的堆棧跟蹤寫入提供的緩沖區。// runtime.Caller//PrintCallerInfo()//time.Sleep(3 * time.Second) // 確保協程有足夠的時間完成//高并發例子FindPrime()//單線程noGoroutine()
    }func FindPrime() {var num intvar n intn, _ = fmt.Scanf("%d", &num)fmt.Println(n, num)n = numif num > 32 { // 當num很大時,增大n(即goroutine的數量)卻沒有帶來性能提升甚至導致耗時n = 32}// 對于num=1000000// n = 1, 即noGoroutine(),大約200ms~270ms// n = 2,大約150ms~250ms// n = 4,大約170ms~270ms// n = 8,大約>220ms// 對于num=10000000// n = 1, 即noGoroutine(),大約5s~6s// n = 2,大約2.7s~2.9s// n = 4,大約1.6s~2.0s// n = 8,大約1.8s~2s// n = 16(開始耗時增加), 大約2.1s~2.2s// 對于num=100000000// n = 1, 即noGoroutine(),>100s// n = 4,大約40s~41s// n = 8,大約25s~26s// n = 16, 大約22s~24s// n = 32,大約·32s~33svar primeChan = make(chan int, num)var inChan = make(chan int, num)var exitChan = make(chan bool, n)var start = time.Now()go inPrime(num, inChan)for i := 0; i < n; i++ {go primeJudge(inChan, primeChan, exitChan)}go func() {for i := 0; i < n; i++ {<-exitChan}close(primeChan)}()for {_, ok := <-primeChanif !ok {break}//fmt.Println(res)}close(exitChan)fmt.Println(time.Since(start))
    }func inPrime(num int, inChan chan int) {for i := 2; i < num; i++ {inChan <- i}close(inChan)
    }func primeJudge(inChan chan int, primeChan chan int, exitChan chan bool) {var flag boolfor {v, ok := <-inChan//fmt.Println("v = ", v, "; ok = ", ok)if !ok {break}flag = truefor i := 2; i < int(math.Sqrt(float64(v))); i++ {if v%i == 0 {flag = falsebreak}}if flag {primeChan <- v}}exitChan <- true
    }func noGoroutine() {var num intn, _ := fmt.Scanf("%d", &num)fmt.Println(n, num)var count = 0var start = time.Now()for i := 2; i <= num; i++ {var flag = truefor j := 2; j < (int)(math.Sqrt(float64(i))); j++ {if i%j == 0 {flag = falsebreak}if flag {count++}}}fmt.Println(time.Since(start))
    }
    

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

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

    相關文章

    Dubbo(30)如何配置Dubbo的服務分片?

    配置Dubbo的服務分片&#xff08;也稱為服務分組&#xff09;可以幫助你將不同的服務實例分組&#xff0c;以實現隔離和管理。通過服務分片&#xff0c;可以在同一個注冊中心中注冊多個相同接口的服務&#xff0c;但它們屬于不同的分組&#xff0c;消費者可以根據需要選擇特定分…

    文檔的預解析

    1. 預解析的核心目標 瀏覽器在正式解析&#xff08;Parsing&#xff09;HTML 前&#xff0c;會啟動一個輕量級的 預解析器&#xff08;Pre-Parser&#xff09;&#xff0c;快速掃描文檔內容&#xff0c;實現&#xff1a; 提前發現并加載關鍵資源&#xff08;如 CSS、JavaScrip…

    通過構造函數和幾何條件,研究了不同函數的最近點存在性、性質及單調性

    解&#xff1a; &#xff08;1&#xff09;對于函數 f ( x ) 1 x f(x) \frac{1}{x} f(x)x1? 和點 M ( 1 , 0 ) M(1, 0) M(1,0)&#xff0c;構造函數 s ( x ) ( x ? 1 ) 2 ( 1 x ) 2 s(x) (x - 1)^2 \left(\frac{1}{x}\right)^2 s(x)(x?1)2(x1?)2。求導得到 s ′ …

    C語言之編譯和debug工具

    gcc gcc是GUN項目為C和C提供的編譯器 入門案例 gcc編譯器最簡單的使用案例&#xff1a;gcc hello.c -o hello&#xff0c;hello.c是源文件&#xff0c;-o參數指定了結果文件的名稱 gcc命令的選項&#xff1a; -v&#xff1a;打印編譯細節-E&#xff1a;僅僅進行預處理&…

    Altshuller矛盾矩陣查詢:基于python和streamlit

    基于python和streamlit實現的Altshuller矛盾矩陣查詢 import streamlit as st import json# 加載數據 st.cache_resource def load_data():with open(parameter.json, encodingutf-8) as f:parameters json.load(f)with open(way.json, encodingutf-8) as f:contradictions …

    Maven的下載配置及在Idea中的配置

    編寫項目管理中存在的問題 在大型Java項目開發中&#xff0c;依賴管理是一個極其復雜的挑戰。傳統方式下&#xff0c;開發者需要手動下載并引入數十甚至上百個JAR包到項目中&#xff0c;這一過程不僅繁瑣低效&#xff0c;還存在諸多痛點&#xff1a; 依賴傳遞性問題&#xff1a…

    來聊聊C++中的vector

    一.vector簡介 vector是什么 C 中的 vector 是一種序列容器&#xff0c;它允許你在運行時動態地插入和刪除元素。 vector 是基于數組的數據結構&#xff0c;但它可以自動管理內存&#xff0c;這意味著你不需要手動分配和釋放內存。 與 C 數組相比&#xff0c;vector 具有更多的…

    WVP-GB28181攝像頭管理平臺存在弱口令

    免責聲明&#xff1a;本號提供的網絡安全信息僅供參考&#xff0c;不構成專業建議。作者不對任何由于使用本文信息而導致的直接或間接損害承擔責任。如涉及侵權&#xff0c;請及時與我聯系&#xff0c;我將盡快處理并刪除相關內容。 漏洞描述 攻擊者可利用漏洞獲取當前系統管…

    訊飛語音聽寫(流式版)開發指南

    語音交互大模型的功能越來越受到重視。訊飛語音聽寫&#xff08;流式版&#xff09;為開發者提供了一種高效、準確的語音識別解決方案。本文將基于 Home.vue、iat_xfyun.js 和 sparkChat.js 這三個文檔&#xff0c;詳細闡述訊飛語音聽寫&#xff08;流式版&#xff09;的開發邏…

    基于kotlin native的C與kotlin互相調用

    本文測試環境為ubuntu&#xff0c;沒有使用IDE&#xff1b;從基本層面了解kotlin native環境中&#xff0c;C和kotlin的編譯&#xff0c;互相調用。 1. kotlin 動態庫 1.1 動態庫編譯 源碼文件libktest.kt&#xff1a; //file name:libktest.kt OptIn(kotlin.experimental.…

    【教學類-102-02】自制剪紙圖案(留白邊、沿線剪)02——Python+PS自動化添加虛線邊框

    背景需求: 01版本實現了對透明背景png圖案邊界線的擴展,黑線實線描邊 【教學類-102-01】自制剪紙圖案(留白邊、沿線剪)01-CSDN博客文章瀏覽閱讀974次,點贊15次,收藏7次。【教學類-102-01】自制剪紙圖案(留白邊、沿線剪)01https://blog.csdn.net/reasonsummer/article…

    Python-函數參數

    1. 參數基礎 函數參數是向函數傳遞數據的主要方式&#xff0c;Python 提供了多種參數傳遞機制。 基本用法 def greet(name): # name 是形式參數print(f"Hello, {name}!")greet("Alice") # "Alice" 是實際參數使用場景&#xff1a;當函數需要…

    《在 Ubuntu 22.04 上安裝 CUDA 11.8 和 Anaconda,并配置環境變量》

    安裝 CUDA 11.8 和 Anaconda 并配置環境變量 在本教程中&#xff0c;我們將介紹如何在 Ubuntu 22.04 上安裝 CUDA 11.8 和 Anaconda&#xff0c;并配置相應的環境變量。我們還將配置使用 阿里云鏡像源 來加速軟件包更新。以下是具體步驟。 步驟 1&#xff1a;更新軟件源 首先…

    Ubuntu環境基于Ollama部署DeepSeek+Open-Webui實現本地部署大模型-無腦部署

    Ollama介紹 Ollama是一款簡單好用的模型部署工具,不僅可以部署DeepSeek,市面上開源模型大部分都可以一鍵部署,這里以DeepSeek為例 官網 DeepSeek 版本硬件要求 安裝Ollama 環境 sudo apt update sudo apt install curl sudo apt install lsof1.命令一鍵安裝 在官網點擊…

    Angular 項目 PDF 批注插件庫在線版 API 示例教程

    本文章介紹 Angular 項目中 PDF 批注插件庫 ElasticPDF 在線版 API 示例教程&#xff0c;API 包含 ① 導出批注后PDF數據&#xff1b;② 導出純批注 json 數據&#xff1b;③ 加載舊批注&#xff1b;④ 切換文檔&#xff1b;⑤ 切換用戶&#xff1b;⑥ 清空批注 等數據處理功能…

    Spring Boot 中利用 Jasypt 實現數據庫字段的透明加密解密

    1. 引言 1.1 什么是 Jasypt Jasypt(Java Simplified Encryption)是一個用于簡化 Java 應用程序中加密操作的庫。 1.2 為什么使用 Jasypt 簡化加密操作:提供簡單的 API 進行加密和解密。透明加密:自動處理加密和解密過程,無需手動干預。多種加密算法:支持多種加密算法,…

    Linux的: /proc/sys/net/ipv6/conf/ 筆記250405

    Linux的: /proc/sys/net/ipv6/conf/ /proc/sys/net/ipv6/conf/ 是 Linux 系統中用于 動態配置 IPv6 網絡接口參數 的核心目錄。它允許針對不同網絡接口&#xff08;如 eth0、wlan0&#xff09;或全局設置&#xff08;all&#xff09;調整 IPv6 協議棧的行為。 它通過虛擬文件系…

    Spring Cloud 框架為什么能處理高并發

    Spring Cloud框架能夠有效處理高并發場景&#xff0c;核心在于其微服務架構設計及多組件的協同作用&#xff0c;具體機制如下&#xff1a; 一、分布式架構設計支撐高擴展性 服務拆分與集群部署 Spring Cloud通過微服務拆分將單體系統解耦為獨立子服務&#xff0c;每個服務可獨…

    無人機智慧路燈桿:智慧城市的‘全能助手’

    在城市發展的進程中&#xff0c;智慧路燈桿作為智慧城市建設的關鍵載體&#xff0c;正逐步從傳統的照明設備轉型為集多種功能于一體的智能基礎設施。無人機與智慧路燈桿的創新性融合&#xff0c;為城市管理和服務帶來了全新的變革與機遇。 一、無人機智慧路燈桿的功能概述 照…

    Libevent UDP開發指南

    UDP 與 TCP 的核心區別 無連接:不需要建立/維護連接 不可靠:不保證數據包順序和到達 高效:頭部開銷小,沒有連接管理負擔 支持廣播/多播:可以向多個目標同時發送數據 一、基礎UDP服務器實現 1. 創建 UDP 套接字 #include <event2/event.h> #include <event2/lis…