最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Go語言中如何通過Goroutine實(shí)現(xiàn)高并發(fā)

 更新時間:2024年10月11日 09:13:16   作者:景天科技苑  
在Go語言中,并發(fā)編程是一個核心且強(qiáng)大的特性,Go語言通過goroutine和channel等機(jī)制,使得并發(fā)編程變得更加簡單和直觀,本文給大家介紹了Go語言中如何通過Goroutine快速實(shí)現(xiàn)高并發(fā),感興趣的小伙伴跟著小編一起來看看吧

一、并發(fā)編程的基本概念

并發(fā)編程是指在一個程序中同時運(yùn)行多個任務(wù),這些任務(wù)可以獨(dú)立地執(zhí)行,也可以相互協(xié)作。并發(fā)編程可以提高程序的執(zhí)行效率,特別是在處理大量I/O操作或計算密集型任務(wù)時。

在Go語言中,并發(fā)編程主要通過goroutine和channel來實(shí)現(xiàn)。
Goroutine是Go語言獨(dú)有的并發(fā)執(zhí)行單元,它允許函數(shù)或方法并發(fā)執(zhí)行,而無需手動管理線程。
Channel是Go語言中進(jìn)行g(shù)oroutine間通信和同步的主要機(jī)制。

二、進(jìn)程、線程、協(xié)程

**程序:**指令和數(shù)據(jù)的一個有序集合。本身沒有任何含義,是一個靜態(tài)的概念。

進(jìn)程:QQ.exe 微信 … 一個個的程序、執(zhí)行起來之后,開啟一個進(jìn)程。執(zhí)行程序的一次執(zhí)行過程,它是動態(tài)的概念。進(jìn)程是系統(tǒng)資源分配的單位。

**線程:**一個進(jìn)程中可以有多個線程,并行的,一個進(jìn)程之中,至少要有一個線程。main 主線程

  • 線程是CPU調(diào)度和執(zhí)行的單位。
  • 一個線程,直接執(zhí)行就可以了
  • 多個線程:CPU如何調(diào)度執(zhí)行。 一個CPU、也是可以跑多個線程的。

并行和并發(fā)

  • 并發(fā):一個cpu同一時間不停執(zhí)行多個程序
  • 并行:多個cpu同一時間不停執(zhí)行多個程序

在這里插入圖片描述

在代碼級別中的所謂多線程并發(fā)處理問題。模擬出來的

  • 真正的多線程是指的擁有多個CPU、多核
  • 如果是模擬出來的多線程、即在一個CPU的情況下,在同一個時間點(diǎn),只能執(zhí)行一個線程的代碼。
  • 因?yàn)閳?zhí)行的速度很快,所以就有了同時在執(zhí)行的一種錯覺。

并行真的就最快嗎?

  • 并行運(yùn)行的組件,考慮到多個線程之間的通信問題,這種跨CPU通信問題是開銷比較高的,并行并不一定快。

在這里插入圖片描述

進(jìn)程

進(jìn)程是一個程序在一個數(shù)據(jù)集中的一次動態(tài)執(zhí)行過程,可以簡單理解為“正在執(zhí)行的程序",它是CPU資源分配和調(diào)度的獨(dú)立單位。

進(jìn)程一般由程序、數(shù)據(jù)集、進(jìn)程控制塊三部分組成。我們編寫的程序用來描述進(jìn)程要完成哪些功能以及如何完成;

數(shù)據(jù)集則是程序在執(zhí)行過程中所需要使用的資源;進(jìn)程控制塊用來記錄進(jìn)程的外部特征,描述進(jìn)程的執(zhí)行變化過程,系統(tǒng)可以利用它來控制和管理進(jìn)程,它是系統(tǒng)感知進(jìn)程存在的唯一標(biāo)志。 進(jìn)程的局限是創(chuàng)建、撤銷和切換的開銷比較大。

線程 :

線程是在進(jìn)程之后發(fā)展出來的概念。線程也叫輕量級進(jìn)程,它是一個基本的CPU執(zhí)行單元,也是程序執(zhí)行過程中的最小單元,由線程ID、 程序計數(shù)器、寄存器集合和堆棧共同組成。一個進(jìn)程可以包含多個線程。

線程的優(yōu)點(diǎn)是減小了程序并發(fā)執(zhí)行時的開銷,提高了操作系統(tǒng)的并發(fā)性能,**缺點(diǎn)是線程沒有自己的系統(tǒng)資源,同一進(jìn)程的各線程可以共享進(jìn)程所擁有的系統(tǒng)資源,如果把進(jìn)程比作一個車間,那么線程就好比是車間里面的工人。**不過對于某些獨(dú)占性資源存在鎖機(jī)制,處理不當(dāng)可能會產(chǎn)生”死鎖"。

協(xié)程Goroutine

協(xié)程是一種用戶態(tài)的輕量級線程,又稱微線程,英文名Coroutine,協(xié)程的調(diào)度完全由用戶控制。人們通常將協(xié)程和子程序(函數(shù))比較著理解。

就好比是啟動了一個函數(shù),單次執(zhí)行完畢它。不影響我們main線程的執(zhí)行。

子程序調(diào)用總是一個入口,一次返回,一旦退出即完成了子程序的執(zhí)行。

與傳統(tǒng)的系統(tǒng)級線程和進(jìn)程相比,協(xié)程的最大優(yōu)勢在于其"輕量級”,可以輕松創(chuàng)建上百萬個而不會導(dǎo)致系統(tǒng)資源衰竭,而線程和進(jìn)程通常最多也不能超過1萬的。這也是協(xié)程也叫輕量級線程的原因。

補(bǔ)充點(diǎn):Go語言流行的主要一個原因,高并發(fā)的問題。高效!

Go語言對于并發(fā)的實(shí)現(xiàn)是靠協(xié)程,Goroutine

三、Goroutine

Go中使用Goroutine來實(shí)現(xiàn)并發(fā)concurrently

**Goroutine是Go語言特有的名詞。**區(qū)別于進(jìn)程Process,線程Thread, 協(xié)程Goroutine, 因?yàn)镚o語言的創(chuàng)造者們覺得和他們是有所區(qū)別的,所以專門創(chuàng)造了Goroutine

Goroutine是與其他函數(shù)或方法同時運(yùn)行的函數(shù)或方法。Goroutines可以被認(rèn)為是輕量級的線程。與線程相比,創(chuàng)建Goroutine的成本很小,它就是一段代碼,一個函數(shù)入口。以及在堆上為其分配的一個堆棧(初始大小為4K,會隨著程序的執(zhí)行自動增長刪除)。因此它非常廉價,Go應(yīng)用程序可以輕松并發(fā)運(yùn)行數(shù)千個Goroutines

在go語言中使用 goroutine,在調(diào)用函數(shù)或者方法前面加上 go 關(guān)鍵字即可。

普通方法調(diào)用 對比 多線程
**普通方法調(diào)用:**串行

main(){ // 串行執(zhí)行 1/2/3
    test1()
    test2()
    test3()
}

多線程

main(){ // 4個線程同時執(zhí)行:main test1  test2 test3 、交替的快速執(zhí)行。
   go test1()
   go test2()
   go test3()
}

在這里插入圖片描述

四、Goroutine的創(chuàng)建和使用

Goroutine是由Go的運(yùn)行時調(diào)度和管理的輕量級線程。每個goroutine都有自己獨(dú)立的棧空間,并且由Go的運(yùn)行時環(huán)境進(jìn)行調(diào)度。
與線程不同,goroutine的創(chuàng)建和切換成本更低,因?yàn)樗皇呛瘮?shù)入口和堆棧的封裝,不需要像線程那樣進(jìn)行復(fù)雜的上下文切換。

1. 創(chuàng)建Goroutine

在Go語言中,使用go關(guān)鍵字來創(chuàng)建一個新的goroutine。例如:

package main

import (
    "fmt"
    "time"
)

func main() {
    //使用go關(guān)鍵字來創(chuàng)建goroutine協(xié)程
    go Hello()
    time.Sleep(1 * time.Second) // 等待一秒鐘,確保Hello函數(shù)有足夠的時間執(zhí)行。如果不加等待,main函數(shù)如果結(jié)束了,所有的 goroutine也會瞬間銷毀
}

func Hello() {
    fmt.Println("hello world")
}

在這里插入圖片描述

2. Goroutine的規(guī)則

1、當(dāng)新的Goroutine開始時, Goroutine調(diào)用立即返回。與函數(shù)不同,go不等待Goroutine執(zhí)行結(jié)束

2、當(dāng)Goroutine調(diào)用,并且Goroutine的任何返回值被忽略之后,go立即執(zhí)行到下一行代碼

3、main的Goroutine應(yīng)該為其他的Goroutines執(zhí)行。如果main的Goroutine終止了,程序?qū)⒈唤K止,而其他Goroutine將不會運(yùn)行

五、Goroutine的生命周期和調(diào)度

Go程序在一個主Goroutine中啟動,這個主Goroutine封裝了main函數(shù)。主Goroutine會進(jìn)行一系列的初始化工作,包括設(shè)置每個goroutine能申請的棧空間的最大尺寸、啟動垃圾回收Goroutine等。

當(dāng)使用go關(guān)鍵字創(chuàng)建一個新的Goroutine時,Go的運(yùn)行時會創(chuàng)建一個新的輕量級線程,并分配一個Goroutine棧,然后將該Goroutine添加到調(diào)度器中。
調(diào)度器會根據(jù)調(diào)度算法選擇一個可用的Goroutine運(yùn)行。如果當(dāng)前沒有可用的Goroutine,程序可能會進(jìn)入休眠狀態(tài)。

Goroutine在調(diào)用某些會引起阻塞的函數(shù)時,會被暫停,直到函數(shù)返回結(jié)果。
這些函數(shù)包括I/O操作、網(wǎng)絡(luò)請求、系統(tǒng)調(diào)用、鎖等。
當(dāng)Goroutine阻塞時,調(diào)度器會將它放回到隊(duì)列中,直到它再次準(zhǔn)備好運(yùn)行。

主Goroutine - mian
封裝main函數(shù)的goroutine稱為主goroutine。
主goroutine所做的事情并不是執(zhí)行main函數(shù)那么簡單。它首先要做的是:設(shè)定每一個goroutine所能申請的??臻g的最大尺寸。
在32位的計算機(jī)系統(tǒng)中此最大尺寸為250MB,而在64位的計算機(jī)系統(tǒng)中此尺寸為1GB。
如果有某個goroutine的??臻g尺寸大于這個限制,那么運(yùn)行時系統(tǒng)就會引發(fā)一個棧溢出(stack overflow)的運(yùn)行時恐慌。隨后,這個go程序的運(yùn)行也會終止。

此后,主goroutine會 進(jìn)行一系列的初始化工作,涉及的工作內(nèi)容大致如下:

1、創(chuàng)建一個特殊的defer語句,用于在主goroutine退出時做必要的善后處理。因?yàn)橹鱣oroutine也可能非正常的結(jié)束

2、啟動專用于在后臺清掃內(nèi)存垃圾的goroutine,并設(shè)置GC可用的標(biāo)識.

3、執(zhí)行main包中所引用包下的init函數(shù)

4、執(zhí)行main函數(shù)

執(zhí)行完main函數(shù)后,它還會檢查主goroutine是否引發(fā)了運(yùn)行時恐慌,并進(jìn)行必要的處理。

程序運(yùn)行完畢后,主goroutine會結(jié)束自己以及當(dāng)前進(jìn)程的運(yùn)行。

六、使用runtime包管理Goroutine

Go語言的runtime包提供了與Go運(yùn)行時環(huán)境交互的各種功能,包括垃圾回收、并發(fā)控制、程序退出、堆棧管理等。以下是一些常用的runtime包函數(shù):

  • runtime.GOMAXPROCS:設(shè)置最大可運(yùn)行的操作系統(tǒng)線程數(shù)。
  • runtime.NumCPU:返回機(jī)器的CPU核心數(shù)。
  • runtime.NumGoroutine:返回當(dāng)前運(yùn)行的Goroutine數(shù)量。
  • runtime.Gosched:讓出CPU時間片,使得其他Goroutine可以運(yùn)行。
  • runtime.Goexit:退出當(dāng)前的Goroutine。
  • runtime.KeepAlive:確保某個Goroutine不會被垃圾回收。
  • runtime.SetFinalizer:為對象設(shè)置終結(jié)器,當(dāng)垃圾回收器準(zhǔn)備回收該對象時,會調(diào)用該終結(jié)器。
  • runtime.GC:強(qiáng)制運(yùn)行垃圾回收器。
  • runtime.GOOS: 獲取操作系統(tǒng)名稱

這些函數(shù)可以幫助我們更好地管理Goroutine和并發(fā)編程。
例如,可以使用runtime.GOMAXPROCS來設(shè)置最大可運(yùn)行的操作系統(tǒng)線程數(shù),從而控制并發(fā)度。
可以使用runtime.NumGoroutine來監(jiān)控當(dāng)前運(yùn)行的Goroutine數(shù)量,以便進(jìn)行性能調(diào)優(yōu)。

獲取系統(tǒng)的信息runtime

package main

import (
    "fmt"
    "runtime"
)

// 獲取系統(tǒng)的信息runtime
func main() {
    // 獲取goRoot目錄 : 找到指定目錄,存放一些項(xiàng)目信息。
    fmt.Println("GoRoot Path:", runtime.GOROOT())
    // 獲取操作系統(tǒng)  windows ,可以根據(jù)操作系統(tǒng)類型判斷盤符分隔符。 “\\”  “/”
    fmt.Println("System:", runtime.GOOS)
    // 獲取cpu數(shù)量 12, 可以嘗試做一些系統(tǒng)優(yōu)化,開啟更大的??臻g。
    fmt.Println("Cpu num:", runtime.NumCPU())
}

在這里插入圖片描述

并發(fā)控制

package main

import (
    "fmt"
    "runtime"
)

// 控制并發(fā)順序
func main() {
    // goroutine是競爭cpu的  ,調(diào)度
    go func() {
        for i := 0; i < 5; i++ {
            fmt.Println("goroutine", i)
        }
    }()

    for i := 0; i < 5; i++ {
        // gosched:禮讓, 讓出時間片,讓其他的 goroutine 先執(zhí)行
        // cpu是隨機(jī),相對來說,可以讓一下,但是不一定能夠成功
        // schedule
        runtime.Gosched()
        fmt.Println("main-", i)
    }
}

正常情況下,main主函數(shù)里面的協(xié)程一般是先于go func()執(zhí)行的,但是我們在main函數(shù)里面加上了runtime.Gosched(),這樣main函數(shù)里面的代碼執(zhí)行就讓出CPU時間片,讓其他Goroutine先執(zhí)行

在這里插入圖片描述

Goroutine的終止和清理

使用runtime.Goexit()終止Goroutine
runtime.Goexit()函數(shù)用于終止當(dāng)前Goroutine的執(zhí)行。當(dāng)調(diào)用Goexit()時,當(dāng)前Goroutine會立即停止執(zhí)行,并把控制權(quán)交還給調(diào)度器。
這意味著當(dāng)前Goroutine不會繼續(xù)執(zhí)行后續(xù)的代碼,也不會返回到調(diào)用它的地方。同時,其他仍在運(yùn)行的Goroutine將繼續(xù)執(zhí)行。例如:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    go func() {
        defer fmt.Println("A.defer")

        func() {
            defer fmt.Println("B.defer")
                        //return // 只是終止了函數(shù)

            //這里設(shè)置,終止當(dāng)前的 goroutine,該Goroutine中下面的代碼不再執(zhí)行
            runtime.Goexit()
            defer fmt.Println("C.defer")
            fmt.Println("B")
        }()

        fmt.Println("A")
    }()

    time.Sleep(1 * time.Second)
}

在這里插入圖片描述

七、多線程會遇到的問題

1. 臨界資源的安全問題

臨界資源:指并發(fā)環(huán)境中多個進(jìn)程、線程、協(xié)程共享的資源

在并發(fā)編程中對臨界資源的處理不當(dāng),往往會導(dǎo)致數(shù)據(jù)不一致的問題。

package main

import (
    "fmt"
    "time"
)

func main() {
    // 此時的a就是臨界資源:多個協(xié)程共享的變量,會導(dǎo)致程序結(jié)果未知
    a := 1

    go func() {
        a = 2
        fmt.Println("goroutine a:", a)
    }()

    a = 3
    time.Sleep(3 * time.Second)
    fmt.Println("main a:", a)
}

在這里插入圖片描述

2. 經(jīng)典案例:售票問題

并發(fā)本身并不復(fù)雜,但是因?yàn)橛辛速Y源競爭的問題,就使得我們開發(fā)出好的并發(fā)程序變得復(fù)雜起來,因?yàn)闀鸷芏嗄涿畹膯栴}。

如果多個goroutine在訪問同一個數(shù)據(jù)資源的時候,其中一個線程修改了數(shù)據(jù),那么這個數(shù)值就被修改了,對于其他的goroutine來講,這個數(shù)值可能是不對的。

package main

import (
    "fmt"
    "time"
)

// 定義全局變量 票庫存為10張
var ticket int = 10

func main() {
    // 單線程不存在問題,多線程資源爭搶就出現(xiàn)了問題
    //多人搶票
    go saleTickets("張三")
    go saleTickets("李四")
    go saleTickets("王五")
    go saleTickets("趙六")

    time.Sleep(time.Second * 5)
}

// 售票函數(shù)
func saleTickets(name string) {
    for {
        if ticket > 0 {
            time.Sleep(time.Millisecond * 1)
            fmt.Println(name, "剩余票的數(shù)量為:", ticket)
            //每賣出一張票,票的數(shù)量減一
            ticket--
        } else {
            fmt.Println("票已售完")
            break
        }
    }
}

多線程都在操作數(shù)據(jù),出現(xiàn)了負(fù)數(shù)這種不合理的結(jié)果

在這里插入圖片描述

發(fā)現(xiàn)結(jié)果和預(yù)想的不同,多線程加入之后,原先單線程的邏輯出現(xiàn)了問題。

以上就是詳解Go語言中如何通過Goroutine實(shí)現(xiàn)高并發(fā)的詳細(xì)內(nèi)容,更多關(guān)于Go Goroutine實(shí)現(xiàn)高并發(fā)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言中的HTTP客戶端從標(biāo)準(zhǔn)庫到第三方庫

    Go語言中的HTTP客戶端從標(biāo)準(zhǔn)庫到第三方庫

    本文介紹了Go語言中的HTTP客戶端開發(fā),涵蓋標(biāo)準(zhǔn)庫和第三方庫使用、最佳實(shí)踐、高級功能、HTTP/2支持、安全考慮、性能優(yōu)化等內(nèi)容,通過本文,讀者可以構(gòu)建高效、可靠的HTTP客戶端應(yīng)用
    2026-05-05
  • goland設(shè)置控制臺折疊效果

    goland設(shè)置控制臺折疊效果

    這篇文章主要介紹了goland設(shè)置控制臺折疊效果,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    Golang中空的切片轉(zhuǎn)化成 JSON 后變?yōu)?nbsp;null 問題的解決方案

    在 Golang 中,經(jīng)常需要將其他類型(例如 slice、map、struct 等類型)的數(shù)據(jù)轉(zhuǎn)化為 JSON 格式,有時候轉(zhuǎn)化的結(jié)果并不是預(yù)期中的,例如將一個空的切片轉(zhuǎn)化為 JSON 時,會變成"null",所以本文將給大家介紹一下解決方法,需要的朋友可以參考下
    2023-09-09
  • Go實(shí)現(xiàn)超時控制的具體使用

    Go實(shí)現(xiàn)超時控制的具體使用

    本文主要介紹了Go中TimeoutControlWrapper實(shí)現(xiàn)超時控制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08
  • Go Vendor 使用指南

    Go Vendor 使用指南

    govendor是一個基于vendor目錄機(jī)制的包管理工具,本文就來詳細(xì)的介紹一下Go Vendor 使用指南,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-04-04
  • Golang 配置和啟動 HTTPS 服務(wù)器

    Golang 配置和啟動 HTTPS 服務(wù)器

    golang-https-example 是一個展示如何在 Go 語言中實(shí)現(xiàn) HTTPS 服務(wù)器的開源項(xiàng)目,該項(xiàng)目通過簡單的代碼示例,幫助開發(fā)者理解如何在 Go 中配置和啟動一個 HTTPS 服務(wù)器,下面就來詳細(xì)的介紹一下
    2026-05-05
  • golang類型轉(zhuǎn)換之interface轉(zhuǎn)字符串string簡單示例

    golang類型轉(zhuǎn)換之interface轉(zhuǎn)字符串string簡單示例

    在我們使用Golang進(jìn)行開發(fā)過程中,總是繞不開對字符或字符串的處理,這篇文章主要給大家介紹了關(guān)于golang類型轉(zhuǎn)換之interface轉(zhuǎn)字符串string的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 深入解析Go語言的io.ioutil標(biāo)準(zhǔn)庫使用

    深入解析Go語言的io.ioutil標(biāo)準(zhǔn)庫使用

    這篇文章主要介紹了Go語言的io.ioutil標(biāo)準(zhǔn)庫使用,是Golang入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-10-10
  • 通過示例深度理解Go channel range

    通過示例深度理解Go channel range

    這篇文章主要為大家介紹了Go channel range使用示例深度理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Go語言學(xué)習(xí)之JSON編碼解析與使用

    Go語言學(xué)習(xí)之JSON編碼解析與使用

    這篇文章主要為大家詳細(xì)介紹了Go語言中JSON編碼的解析與使用已經(jīng)JSON與Map、結(jié)構(gòu)體的互相轉(zhuǎn)化,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-02-02

最新評論

孝昌县| 萨嘎县| 石河子市| 涿州市| 清水县| 长葛市| 海宁市| 绍兴县| 成安县| 九龙县| 涞水县| 莱芜市| 新源县| 三亚市| 汽车| 惠东县| 五原县| 宁乡县| 克拉玛依市| 禄丰县| 拜泉县| 彭州市| 务川| 奉节县| 泾川县| 合肥市| 佛冈县| 石泉县| 绥滨县| 泗阳县| 吉安市| 雷波县| 水富县| 区。| 句容市| 祁阳县| 洪雅县| 蓝田县| 金阳县| 大新县| 红安县|