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

Go 并發(fā)編程Goroutine的實(shí)現(xiàn)示例

 更新時間:2024年12月16日 11:16:27   作者:比豬聰明  
Go語言中的并發(fā)編程主要通過Goroutine和Channel來實(shí)現(xiàn),本文就來介紹一下Go 并發(fā)編程的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

進(jìn)程(Process),線程(Thread),協(xié)程(Goroutine,也叫輕量級線程)

進(jìn)程

進(jìn)程是一個程序在一個數(shù)據(jù)集中的一次動態(tài)執(zhí)行過程,進(jìn)程一般由程序,數(shù)據(jù)集,進(jìn)程控制塊三部分組成

線程

線程也叫輕量級進(jìn)程,他是一個基本的CPU執(zhí)行單元,也就是程序執(zhí)行過程中的最小單元,由線程ID,程序計(jì)數(shù)器,寄存器集合和堆棧共同組成的,一個進(jìn)程可以包含多個線程

協(xié)程

協(xié)程是一種用戶態(tài)的輕量級線程,又稱微線程,協(xié)程的調(diào)度完全由用戶控制

一、主Goroutine

封裝main函數(shù)的Goroutine被稱為主Goroutine

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

此后主Goroutine會進(jìn)行一系列的初始化工作:

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

2、啟動專用于在后臺清掃內(nèi)存垃圾的Goroutine,并設(shè)置GC可用的表示

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

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

二、Goroutine

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

Goroutine是與其他函數(shù)或方法同時運(yùn)行的函數(shù)或方法,與線程相比創(chuàng)建goroutine的成本很小,他就是一段代碼,一個函數(shù)入口,以及在堆上為其分配一個堆棧(初始大小為4k,會隨著程序的執(zhí)行自動增長刪除)。

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

package main

import "fmt"

func main() {

	// 使用go關(guān)鍵字使用goroutine調(diào)用hello函數(shù)
	go hello()
	for i := 0; i < 150000; i++ {
		//fmt.Println("main-", i)
	}
}

func hello() {
	for i := 0; i < 10; i++ {
		fmt.Println("hello-----------", i)
	}
}

/*
    此處代碼可設(shè)置main協(xié)程for循環(huán)次數(shù)的大小觀測go協(xié)程調(diào)用hello情況
*/
  • 當(dāng)新的Goroutine開始時,Goroutine調(diào)用立即返回,與函數(shù)不同,go不等待Goroutine執(zhí)行結(jié)束

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

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

三、runtime

  • 獲取系統(tǒng)信息

  • schedule調(diào)度讓出時間片,讓別的goroutine先執(zhí)行

  • Goexit   //終止當(dāng)前的goroutine 

Go 語言的 runtime 包提供了與 Go 運(yùn)行時環(huán)境交互的各種功能。這個包允許你控制和檢查程序的運(yùn)行時行為,包括但不限于:

  • 垃圾回收(Garbage Collection):可以手動觸發(fā)垃圾回收,或者調(diào)整垃圾回收的策略。

  • 并發(fā)控制:提供了包括 Gosched() 在內(nèi)的方法來控制 goroutine 的調(diào)度。

  • 程序退出:可以正常或非正常地退出程序。

  • 堆棧管理:可以獲取當(dāng)前 goroutine 的堆棧信息。

  • 環(huán)境變量:讀取和設(shè)置環(huán)境變量。

  • 系統(tǒng)信號:處理操作系統(tǒng)信號。

  • CPU 信息:獲取 CPU 的數(shù)量和相關(guān)信息。

  • 內(nèi)存分配:可以手動分配和釋放內(nèi)存。

  • 性能監(jiān)控:可以監(jiān)控程序的 CPU 使用情況。

以下是一些 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)行垃圾回收器。 
package main

import (
	"fmt"
	"runtime"
)

func main() {

	//獲取系統(tǒng)信息
	fmt.Println("獲取GOROOT目錄", runtime.GOROOT())
	fmt.Println("獲取操作系統(tǒng)", runtime.GOOS)
	fmt.Println("獲取CPU", runtime.NumCPU())

	//Goroutine 調(diào)度
	go func() {
		for i := 0; i < 100; i++ {
			fmt.Println("Goroutine---", i)
		}
	}()

	for i := 0; i < 100; i++ {
		//讓出時間片,讓別的Goroutine先執(zhí)行,不一定可以讓成功
		runtime.Gosched()
		fmt.Println("main---", i)
	}
}

runtime.Gosched() 是 Go 語言運(yùn)行時庫中的一個函數(shù),它用于讓出 CPU 時間片,讓其他 goroutine(輕量級線程)有機(jī)會執(zhí)行。這通常用于避免阻塞或減少阻塞的持續(xù)時間,尤其是在長時間運(yùn)行的 goroutine 中,你可能會在適當(dāng)?shù)牡胤秸{(diào)用 Gosched 來讓出 CPU,以避免長時間占用 CPU 導(dǎo)致其他 goroutine 饑餓。

以下是 runtime.Gosched() 函數(shù)的一些使用場景:

  • 避免饑餓:在長時間運(yùn)行的循環(huán)中,如果確定當(dāng)前 goroutine 可能不會被阻塞,可以調(diào)用 Gosched 來讓出 CPU。

  • 控制執(zhí)行順序:在某些情況下,你可能希望控制 goroutine 的執(zhí)行順序,通過 Gosched 可以給其他 goroutine 運(yùn)行的機(jī)會。

  • 減少 CPU 使用:在某些 I/O 密集型操作中,如果當(dāng)前 goroutine 主要是等待 I/O 操作完成,調(diào)用 Gosched 可以讓出 CPU,減少不必要的 CPU 使用。

  • 避免死鎖:在某些復(fù)雜的 goroutine 調(diào)度中,如果擔(dān)心死鎖問題,可以在適當(dāng)?shù)牡胤秸{(diào)用 Gosched 來減少死鎖的風(fēng)險(xiǎn)。 

package main

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

func main() {
	/*
		因?yàn)間oroutine2延時了一定時間,如果goroutine1不讓出CPU時間片那么必先執(zhí)行完成
	*/
	go func() {
		for i := 0; i < 5; i++ {
			runtime.Gosched() // 讓出 CPU 時間片
			fmt.Println("Goroutine 1:", i)
		}
	}()

	go func() {
		for i := 0; i < 5; i++ {
			time.Sleep(100 * time.Millisecond)
			fmt.Println("Goroutine 2:", i)
		}
	}()

	time.Sleep(3 * time.Second) // 等待兩個 goroutine 執(zhí)行完畢
}

請注意,過度使用 Gosched 可能會導(dǎo)致性能下降,因?yàn)轭l繁的調(diào)度會消耗額外的 CPU 資源。因此,應(yīng)該在仔細(xì)考慮后,根據(jù)實(shí)際需要來使用 Gosched

查看協(xié)程數(shù)&CUP數(shù)

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	var wg sync.WaitGroup //創(chuàng)建并發(fā)組
	fmt.Printf("當(dāng)前運(yùn)行的goroutine數(shù)量: %d\n", runtime.NumGoroutine())
	//創(chuàng)建10個協(xié)程
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			fmt.Printf("第 %d 個協(xié)程在 running\n", id)
		}(i)
	}
	fmt.Printf("當(dāng)前運(yùn)行的goroutine數(shù)量: %d\n", runtime.NumGoroutine())
	wg.Wait()

	fmt.Printf("CPU核心數(shù): %d\n", runtime.NumCPU())
}

四、互斥鎖 

在并發(fā)編程中會遇到的臨界資源安全問題,可以采用互斥鎖的方式來解決,后面也可通過通過通道channel來解決

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

使用sync包下的鎖解決臨界資源安全問題(Mutex) 

package main

import (
	"fmt"
	"sync"
	"time"
)

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

// 創(chuàng)建鎖
var mutexs sync.Mutex

func main() {
	//三個窗口同時售票
	go saleTicket("售票口1")
	go saleTicket("售票口2")
	go saleTicket("售票口3")
	time.Sleep(time.Second * 15) //等待售票完

}

// 售票函數(shù)
func saleTicket(name string) {
	for {
		// 在檢查之前上鎖
		mutexs.Lock()
		if tickets > 0 {
			time.Sleep(time.Second)
			fmt.Printf("%s剩余的票數(shù)為:%d\n", name, tickets)
			tickets--
		} else {
			fmt.Println("票已售完")
			break
		}
		//操作結(jié)束后解鎖
		mutexs.Unlock()
	}
}

sync包下的同步等待組(WaitGroup)

package main

import (
	"fmt"
	"sync"
	"time"
)

var w sync.WaitGroup

func main() {
	// 公司最后關(guān)門的人   0
	// wg.Add(2) 判斷還有幾個線程、計(jì)數(shù)  num=2
	// wg.Done() 我告知我已經(jīng)結(jié)束了  -1
	w.Add(2)

	go test11()
	go test22()

	fmt.Println("main等待ing")
	w.Wait() // 等待 wg 歸零,才會繼續(xù)向下執(zhí)行
	fmt.Println("end")

	// 理想狀態(tài):所有協(xié)程執(zhí)行完畢之后,自動停止。
	//time.Sleep(3 * time.Second)

}
func test11() {
	for i := 0; i < 5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Println("test1--", i)
	}
	w.Done()
}
func test22() {
	defer w.Done()
	for i := 0; i < 5; i++ {
		fmt.Println("test2--", i)
	}
}

五、Channel通道 

 不要以共享內(nèi)存的方式通信,而要以通信的方式共享內(nèi)存

 通道可以被認(rèn)為是Goroutines通信的管道,類似于管道中的水從一端到另一端的流動,數(shù)據(jù)可以從一端發(fā)送到另一端,通過通道接收,GO語言中建議使用Channel通道來實(shí)現(xiàn)Goroutines之間的通信

GO從語言層面保證同一個時間只有一個goroutine能夠訪問channel里面的數(shù)據(jù),使用channel來通信,通過通信來傳遞內(nèi)存數(shù)據(jù),使得內(nèi)存數(shù)據(jù)在不同的goroutine中傳遞,而不是使用共享內(nèi)存來通信

每個通道都有與其相關(guān)的類型,類型是通道允許傳輸?shù)臄?shù)據(jù)類型(通道的零值為nil,nil通道沒有任何用處,因此通道必須使用類似于map和切片的方法定義)

一個通道發(fā)送和接收數(shù)據(jù)默認(rèn)是阻塞的,當(dāng)一個數(shù)據(jù)被發(fā)送到通道時,在發(fā)送語句中被阻塞,直到另一個Goroutine從通道中讀取數(shù)據(jù) 

關(guān)閉通道

發(fā)送者可以通過關(guān)閉通道來通知接收方不會有更多的數(shù)據(jù)被發(fā)送到通道

close(ch)

 接收者可以在接收來自通道的數(shù)據(jù)時使用額外的變量來檢查通道是否已關(guān)閉

v,ok := <- ch

當(dāng)ok的值為true,表示成功的從通道中讀取了一個數(shù)據(jù)value,通道關(guān)閉時仍然可以讀(存)數(shù)據(jù)當(dāng)ok的值為false,表示從一個封閉的通道讀取數(shù)據(jù),從閉通道讀取的數(shù)據(jù)將是通道類型的零值 

緩沖通道

緩沖通道是指一個通道,帶有一個緩沖區(qū),發(fā)送到一個緩沖通道只有在緩沖區(qū)滿時才被阻塞,類似的,從緩沖通道接收的信息只有在為空時才會被阻塞,可以通過將額外的容量參數(shù)傳遞給make函數(shù)來創(chuàng)建緩沖通道,該函數(shù)指定緩沖區(qū)的大小

package main

import (
	"fmt"
	"strconv"
	"time"
)

func main() {
	//定義通道可以寫10個數(shù)據(jù)
	ch := make(chan string, 10)
	go test3(ch)

	for v := range ch {
		fmt.Println(v)
	}
}

func test3(ch chan string) {
	for i := 0; i < 5; i++ {
		time.Sleep(time.Second)
		fmt.Println("通道內(nèi)寫入數(shù)據(jù)", "tset--"+strconv.Itoa(i))
		ch <- "tset--" + strconv.Itoa(i)
	}
	close(ch)//如果不關(guān)閉協(xié)程,主協(xié)程的for循環(huán)一值阻塞,知道報(bào)錯“fatal error: all goroutines are asleep - deadlock!”
}

定向通道

單向通道也就是定向通道,這些通道只能發(fā)送數(shù)據(jù)或者接收數(shù)據(jù) 

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int)
	go writerOnly(ch)
	go readOnly(ch)

	time.Sleep(time.Second * 2)
}

// 只讀,指只允許管道讀入/寫出數(shù)據(jù)
func writerOnly(ch chan<- int) {
	ch <- 10
}

// 只寫,指指只允許管道讀出/寫入數(shù)據(jù)
func readOnly(ch <-chan int) {
	temp := <-ch
	fmt.Println(temp)
}

Select

  • 每個case都必須是一個通道的操作

  • 如果任意某個通信可以進(jìn)行,他就執(zhí)行,其他被忽略

  • 如果有多個case都可以運(yùn)行,Select會隨機(jī)公平的選出一個執(zhí)行

否則

  • 如果有default子句,則執(zhí)行該語句

  • 如果沒有default字句,select將阻塞,直到某個通信可以運(yùn)行,GO不會重新對channel或值進(jìn)行求值

package main

import (
	"fmt"
	"time"
)

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		time.Sleep(time.Second * 2)
		ch1 <- 100
	}()

	go func() {
		time.Sleep(time.Second * 2)
		ch2 <- 200
	}()

	select {
	case num1 := <-ch1:
		fmt.Println("ch1--", num1)
	case num2 := <-ch2:
		fmt.Println("ch2--", num2)
	}
    //沒有default,則等待通道(阻塞),因?yàn)閟elect{}本身是阻塞的
}

 利用通道解決臨界資源安全問題

package main

import (
	"fmt"
	"sync"
	"time"
)

// 定義全局chan,存儲票總數(shù)
var totalTickets chan int
var wg sync.WaitGroup

func main() {

	// 初始化票數(shù)量:總票數(shù)10張
	totalTickets = make(chan int, 2)
	totalTickets <- 10

	wg.Add(3)

	go sell("售票口1")
	go sell("售票口2")
	go sell("售票口3")

	wg.Wait()

	fmt.Println("買完了,下班")

}

func sell(name string) {
	defer wg.Done()

	for { //for循環(huán)表示一直在賣,一直在營業(yè)
		residue, ok := <-totalTickets
		if !ok {
			fmt.Printf("%s: 關(guān)閉\n", name)
			break
		}
		if residue > 0 {
			time.Sleep(time.Second * 1)
			totalTickets <- residue - 1 //
			fmt.Println(name, "售出1張票,余票:", residue)
		} else {
			//進(jìn)入此處時票已經(jīng)買完了,因?yàn)閒or循環(huán)一進(jìn)來售票窗口就檢查是否還有票,假如最后賣完的是售票口3,那么銷售窗口1跟2就會判斷還有沒有
			fmt.Printf("%s: 關(guān)閉\n", name)
			close(totalTickets)
			break
		}
	}
}

到此這篇關(guān)于Go 并發(fā)編程Goroutine的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Go 并發(fā)Goroutine內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 使用Go語言自定義隊(duì)列結(jié)構(gòu)的示例代碼

    使用Go語言自定義隊(duì)列結(jié)構(gòu)的示例代碼

    這篇文章主要介紹了自定義隊(duì)列結(jié)構(gòu) 的完整內(nèi)容,幫助初學(xué)者通過自定義結(jié)構(gòu)體來實(shí)現(xiàn)隊(duì)列的數(shù)據(jù)結(jié)構(gòu),文中有詳細(xì)的示例代碼供大家參考,需要的朋友可以參考下
    2025-07-07
  • Go語言中循環(huán)Loop的用法介紹

    Go語言中循環(huán)Loop的用法介紹

    這篇文章介紹了Go語言中循環(huán)Loop的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • Golang實(shí)現(xiàn)Directional Channel(定向通道)

    Golang實(shí)現(xiàn)Directional Channel(定向通道)

    這篇文章主要介紹了Golang實(shí)現(xiàn)Directional Channel(定向通道),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • go中switch語句的用法詳解

    go中switch語句的用法詳解

    在Go中的switch語句類似于C、C++、Java、JavaScript和PHP中的switch語句,不同之處在于它只執(zhí)行匹配的case,因此不需要使用break語句,下面我們就一起來學(xué)習(xí)一下switch語句的具體使用吧
    2023-09-09
  • Go作用域的理解

    Go作用域的理解

    在Go語言中,作用域是指變量的可見性范圍,它定義了變量在程序中的生命周期和可訪問性,本文主要介紹了Go作用域的理解,包括局部作用域、全局作用域、命名空間作用域等,感興趣的可以了解一下
    2023-11-11
  • 詳解如何通過Go來操作Redis實(shí)現(xiàn)簡單的讀寫操作

    詳解如何通過Go來操作Redis實(shí)現(xiàn)簡單的讀寫操作

    作為最常用的分布式緩存中間件——Redis,了解運(yùn)作原理和如何使用是十分有必要的,今天來學(xué)習(xí)如何通過Go來操作Redis實(shí)現(xiàn)基本的讀寫操作,需要的朋友可以參考下
    2023-09-09
  • 如何在Go語言中高效使用Redis的Pipeline

    如何在Go語言中高效使用Redis的Pipeline

    在 Redis 中,Pipeline 就像一條流水線,它允許我們將多個命令一次性發(fā)送到服務(wù)器,下面我們就來看看如何在Go語言中高效使用Redis的Pipeline吧
    2024-11-11
  • 基于Go語言開發(fā)一個Markdown轉(zhuǎn)HTML工具

    基于Go語言開發(fā)一個Markdown轉(zhuǎn)HTML工具

    這篇文章主要為大家詳細(xì)介紹了如何基于Go語言開發(fā)一個Markdown轉(zhuǎn)HTML工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-09-09
  • Golang初始化MySQL數(shù)據(jù)庫方法淺析

    Golang初始化MySQL數(shù)據(jù)庫方法淺析

    這篇文章主要介紹了Golang初始化MySQL數(shù)據(jù)庫的方法,數(shù)據(jù)庫的建立第一步即要初始化,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-05-05
  • golang unique包和字符串內(nèi)部化優(yōu)化技巧

    golang unique包和字符串內(nèi)部化優(yōu)化技巧

    最近在做老系統(tǒng)優(yōu)化,正好遇到了需要使用字符串內(nèi)部化的場景,所以今天就來說說字符串內(nèi)部化這種優(yōu)化技巧,對golang unique包和字符串內(nèi)部化優(yōu)化技巧感興趣的朋友跟隨小編一起看看吧
    2025-10-10

最新評論

长汀县| 九龙城区| 阜阳市| 高尔夫| 天台县| 博罗县| 梁河县| 乐至县| 枝江市| 珲春市| 佳木斯市| 西乌珠穆沁旗| 鄂伦春自治旗| 焉耆| 射洪县| 永清县| 桐乡市| 崇左市| 灌阳县| 大连市| 思茅市| 阿拉尔市| 福海县| 应用必备| 安乡县| 临颍县| 会宁县| 新邵县| 正镶白旗| 同心县| 新野县| 舟曲县| 重庆市| 枞阳县| 东乡族自治县| 岑巩县| 余江县| 安龙县| 开原市| 莱阳市| 子洲县|