欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

二、進程、線程、協(xié)程

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

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

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

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

并行和并發(fā)

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

在這里插入圖片描述

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

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

并行真的就最快嗎?

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

在這里插入圖片描述

進程

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

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

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

線程 :

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

線程的優(yōu)點是減小了程序并發(fā)執(zhí)行時的開銷,提高了操作系統(tǒng)的并發(fā)性能,**缺點是線程沒有自己的系統(tǒng)資源,同一進程的各線程可以共享進程所擁有的系統(tǒ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)級線程和進程相比,協(xié)程的最大優(yōu)勢在于其"輕量級”,可以輕松創(chuàng)建上百萬個而不會導致系統(tǒng)資源衰竭,而線程和進程通常最多也不能超過1萬的。這也是協(xié)程也叫輕量級線程的原因。

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

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

三、Goroutine

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

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

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

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

普通方法調(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的運行時調(diào)度和管理的輕量級線程。每個goroutine都有自己獨立的??臻g,并且由Go的運行時環(huán)境進行調(diào)度。
與線程不同,goroutine的創(chuàng)建和切換成本更低,因為它只是函數(shù)入口和堆棧的封裝,不需要像線程那樣進行復雜的上下文切換。

1. 創(chuàng)建Goroutine

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

package main

import (
    "fmt"
    "time"
)

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

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

在這里插入圖片描述

2. Goroutine的規(guī)則

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

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

3、main的Goroutine應該為其他的Goroutines執(zhí)行。如果main的Goroutine終止了,程序將被終止,而其他Goroutine將不會運行

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

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

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

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

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

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

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

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

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

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

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

程序運行完畢后,主goroutine會結束自己以及當前進程的運行。

六、使用runtime包管理Goroutine

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

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

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

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

package main

import (
    "fmt"
    "runtime"
)

// 獲取系統(tǒng)的信息runtime
func main() {
    // 獲取goRoot目錄 : 找到指定目錄,存放一些項目信息。
    fmt.Println("GoRoot Path:", runtime.GOROOT())
    // 獲取操作系統(tǒng)  windows ,可以根據(jù)操作系統(tǒng)類型判斷盤符分隔符。 “\\”  “/”
    fmt.Println("System:", runtime.GOOS)
    // 獲取cpu數(shù)量 12, 可以嘗試做一些系統(tǒng)優(yōu)化,開啟更大的棧空間。
    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是隨機,相對來說,可以讓一下,但是不一定能夠成功
        // 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ù)用于終止當前Goroutine的執(zhí)行。當調(diào)用Goexit()時,當前Goroutine會立即停止執(zhí)行,并把控制權交還給調(diào)度器。
這意味著當前Goroutine不會繼續(xù)執(zhí)行后續(xù)的代碼,也不會返回到調(diào)用它的地方。同時,其他仍在運行的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ù)

            //這里設置,終止當前的 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)境中多個進程、線程、協(xié)程共享的資源

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

package main

import (
    "fmt"
    "time"
)

func main() {
    // 此時的a就是臨界資源:多個協(xié)程共享的變量,會導致程序結果未知
    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ā)出好的并發(fā)程序變得復雜起來,因為會引起很多莫名其妙的問題。

如果多個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)了負數(shù)這種不合理的結果

在這里插入圖片描述

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

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

相關文章

  • Go語言使用templ實現(xiàn)編寫HTML用戶界面

    Go語言使用templ實現(xiàn)編寫HTML用戶界面

    templ是一個在 Go 中編寫 HTML 用戶界面的語言,使用 templ,我們可以創(chuàng)建可呈現(xiàn) HTML 片段的組件,下面就跟隨小編一起了解一下具體的實現(xiàn)方法吧
    2023-12-12
  • golang 兩個go程輪流打印一個切片的實現(xiàn)

    golang 兩個go程輪流打印一個切片的實現(xiàn)

    這篇文章主要介紹了golang 兩個go程輪流打印一個切片的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-10-10
  • golang版本升級的簡單實現(xiàn)步驟

    golang版本升級的簡單實現(xiàn)步驟

    個人感覺Go在眾多高級語言中,是在各方面都比較高效的,下面這篇文章主要給大家介紹了關于golang版本升級的簡單實現(xiàn)步驟,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • go語言中的json與map相互轉換實現(xiàn)

    go語言中的json與map相互轉換實現(xiàn)

    本文主要介紹了go語言中的json與map相互轉換實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Golang之defer 延遲調(diào)用操作

    Golang之defer 延遲調(diào)用操作

    這篇文章主要介紹了Golang之defer 延遲調(diào)用操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言模擬while語句實現(xiàn)無限循環(huán)的方法

    Go語言模擬while語句實現(xiàn)無限循環(huán)的方法

    這篇文章主要介紹了Go語言模擬while語句實現(xiàn)無限循環(huán)的方法,實例分析了for語句模擬while語句的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Golang無限緩存channel的設計與實現(xiàn)解析

    Golang無限緩存channel的設計與實現(xiàn)解析

    這篇文章主要為大家介紹了Golang無限緩存channel的設計與實現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • Golang 串口通信的實現(xiàn)示例

    Golang 串口通信的實現(xiàn)示例

    串口通信是一種常見的硬件通信方式,用于在計算機和外部設備之間傳輸數(shù)據(jù),本文主要介紹了Golang 串口通信的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • go語言LeetCode題解720詞典中最長的單詞

    go語言LeetCode題解720詞典中最長的單詞

    這篇文章主要為大家介紹了go語言LeetCode題解720詞典中最長的單詞,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • golang如何去除 context 的 deadline

    golang如何去除 context 的 deadline

    在使用 context 的時候遇到了開協(xié)程處理任務的情況,但是直接在協(xié)程里使用主線程的 context 會導致當主線程返回時協(xié)程任務也會因為 context cancel 而失敗,本文提供了兩種辦法可以取消掉 context 里的 timeout 和 deadline,再設置一個新的 timeout 上去
    2023-03-03

最新評論