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

淺談Go語(yǔ)言高并發(fā)處理思路

 更新時(shí)間:2025年11月21日 09:31:28   作者:好易學(xué)·數(shù)據(jù)結(jié)構(gòu)  
本文主要介紹了Go語(yǔ)言高并發(fā)處理思路,通過使用goroutine和channel,可以實(shí)現(xiàn)高效的異步處理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Go語(yǔ)言作為新興的語(yǔ)言,最近發(fā)展勢(shì)頭很是迅猛,其最大的特點(diǎn)就是原生支持并發(fā)。它使用的是“協(xié)程(goroutine)模型”,和傳統(tǒng)基于 OS 線程和進(jìn)程實(shí)現(xiàn)不同,Go語(yǔ)言的并發(fā)是基于用戶態(tài)的并發(fā),這種并發(fā)方式就變得非常輕量,能夠輕松運(yùn)行幾萬并發(fā)邏輯。Go 的并發(fā)屬于 CSP 并發(fā)模型的一種實(shí)現(xiàn),CSP 并發(fā)模型的核心概念是:“不要通過共享內(nèi)存來通信,而應(yīng)該通
過通信來共享內(nèi)存”。這在 Go 語(yǔ)言中的實(shí)現(xiàn)就是 Goroutine 和 Channel。

1、場(chǎng)景描述

在一些場(chǎng)景下,有大規(guī)模請(qǐng)求(十萬或百萬級(jí)qps),我們處理的請(qǐng)求可能不需要立馬知道結(jié)果,例如數(shù)據(jù)的打點(diǎn),文件的上傳等等。這時(shí)候我們需要異步化處理。常用的方法有使用resque、MQ、RabbitMQ等。這里我們?cè)贕olang語(yǔ)言里進(jìn)行設(shè)計(jì)實(shí)踐。

2、方案演進(jìn)

2.1、直接使用goroutine

在Go語(yǔ)言原生并發(fā)的支持下,我們可以直接使用一個(gè)goroutine(如下方式)去并行處理這個(gè)請(qǐng)求。但是,這種方法明顯有些不好的地方,我們沒法控制goroutine產(chǎn)生數(shù)量,如果處理程序稍微耗時(shí),在單機(jī)萬級(jí)十萬級(jí)qps請(qǐng)求下,goroutine大規(guī)模爆發(fā),內(nèi)存暴漲,處理效率會(huì)很快下降甚至引發(fā)程序崩潰。

2.2、緩沖隊(duì)列

緩沖隊(duì)列一定程度上了提高了并發(fā),但也是治標(biāo)不治本,大規(guī)模并發(fā)只是推遲了問題的發(fā)生時(shí)間。當(dāng)請(qǐng)求速度遠(yuǎn)大于隊(duì)列的處理速度時(shí),緩沖區(qū)很快被打滿,后面的請(qǐng)求一樣被堵塞了。

2.3  隊(duì)列+工作池

只用緩沖隊(duì)列不能解決根本問題,這時(shí)候我們可以參考一下線程池的概念,定一個(gè)工作池(協(xié)程池),來限定最大goroutine數(shù)目。每次來新的job時(shí),從工作池里取出一個(gè)可用的worker來執(zhí)行job。這樣一來即保障了goroutine的可控性,也盡可能大的提高了并發(fā)處理能力。

3、代碼實(shí)現(xiàn)思路:

首先,我們定義一個(gè)job的接口, 具體內(nèi)容由具體job實(shí)現(xiàn);

// --------------------------- Job ---------------------
type Job interface {
	Do()
}

然后定義一下job隊(duì)列和work池類型,這里我們work池也用golang的channel實(shí)現(xiàn)。

type JobQueue chan Job
// --------------------------- Worker ---------------------
type Worker struct {
	JobChan JobQueue //每一個(gè)worker對(duì)象具有JobQueue(隊(duì)列)屬性。
}
// --------------------------- WorkerPool ---------------------
type WorkerPool struct { //線程池:
	Workerlen   int //線程池的大小
	JobQueue    JobQueue //Job隊(duì)列,接收外部的數(shù)據(jù)
	WorkerQueue chan JobQueue //worker隊(duì)列:處理任務(wù)的Go程隊(duì)列
}

4、完整代碼

package main

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

//定義一個(gè)實(shí)現(xiàn)Job接口的數(shù)據(jù)
type Score struct {
	Num int
}
//定義對(duì)數(shù)據(jù)的處理
func (s *Score) Do() {
	fmt.Println("num:", s.Num)
	time.Sleep(500*time.Millisecond) //模擬執(zhí)行的耗時(shí)任務(wù)
}

func main() {
	num := 100 * 100 * 2 //開啟 2萬個(gè)線程
	// debug.SetMaxThreads(num + 1000) //設(shè)置最大線程數(shù)
	// 注冊(cè)工作池,傳入任務(wù)
	// 參數(shù)1 worker并發(fā)個(gè)數(shù)
	p := NewWorkerPool(num)
	p.Run()

	//寫入一千萬條數(shù)據(jù)
	dataNum := 100 * 100* 100* 10
	go func() {
		for i := 1; i <= dataNum; i++ {
			sc := &Score{Num: i}
			p.JobQueue <- sc //數(shù)據(jù)傳進(jìn)去會(huì)被自動(dòng)執(zhí)行Do()方法,具體對(duì)數(shù)據(jù)的處理自己在Do()方法中定義
		}
	}()
	//循環(huán)打印輸出當(dāng)前進(jìn)程的Goroutine 個(gè)數(shù)
	for {
		fmt.Println("runtime.NumGoroutine() :", runtime.NumGoroutine())
		time.Sleep(5 * time.Second)
	}

}
// --------------------------- Job ---------------------
type Job interface {
	Do()
}
type JobQueue chan Job
// --------------------------- Worker ---------------------
type Worker struct {
	JobChan JobQueue //每一個(gè)worker對(duì)象具有JobQueue(隊(duì)列)屬性。
}

func NewWorker() Worker {
	return Worker{JobChan: make(chan Job)}
}
//啟動(dòng)參與程序運(yùn)行的Go程數(shù)量
func (w Worker) Run(wq chan JobQueue) {
	go func() {
		for {
		    wq <- w.JobChan //處理任務(wù)的Go程隊(duì)列數(shù)量有限,每運(yùn)行1個(gè),向隊(duì)列中添加1個(gè),隊(duì)列剩余數(shù)量少1個(gè) (JobChain入隊(duì)列)
			select {
			case job := <-w.JobChan:
				//fmt.Println("xxx2:",w.JobChan)
				job.Do() //執(zhí)行操作
			}
		}
	}()
}

// --------------------------- WorkerPool ---------------------
type WorkerPool struct { //線程池:
	Workerlen   int //線程池的大小
	JobQueue    JobQueue //Job隊(duì)列,接收外部的數(shù)據(jù)
	WorkerQueue chan JobQueue //worker隊(duì)列:處理任務(wù)的Go程隊(duì)列
}

func NewWorkerPool(workerlen int) *WorkerPool {
	return &WorkerPool{
		Workerlen:   workerlen,
		JobQueue:    make(JobQueue),
		WorkerQueue: make(chan JobQueue, workerlen),
	}
}
func (wp *WorkerPool) Run() {
	fmt.Println("初始化worker")
	//初始化worker(多個(gè)Go程)
	for i := 0; i < wp.Workerlen; i++ {
		worker := NewWorker()
		worker.Run(wp.WorkerQueue) //開啟每一個(gè)Go程
	}
	// 循環(huán)獲取可用的worker,往worker中寫job
	go func() {
		for {
			select {
			//將JobQueue中的數(shù)據(jù)存入WorkerQueue
			case job := <-wp.JobQueue: //線程池中有需要待處理的任務(wù)(數(shù)據(jù)來自于請(qǐng)求的任務(wù)) :讀取JobQueue中的內(nèi)容
				worker := <-wp.WorkerQueue //隊(duì)列中有空閑的Go程   :讀取WorkerQueue中的內(nèi)容,類型為:JobQueue
				worker <- job //空閑的Go程執(zhí)行任務(wù)  :整個(gè)job入隊(duì)列(channel) 類型為:傳遞的參數(shù)(Score結(jié)構(gòu)體)
				//fmt.Println("xxx1:",worker)
				//fmt.Printf("====%T  ;  %T======\n",job,worker,)
			}
		}
	}()
}

運(yùn)行效果:

        

5、資源消耗

5.1  CPU消耗對(duì)比

5.2  內(nèi)存消耗對(duì)比

6、代碼分析

核心代碼:

思考:臨時(shí)變量 worker是channel,沒有讀操作,只有寫操作。為什么沒有發(fā)生死鎖現(xiàn)象?

select {
			case job := <-wp.JobQueue: 
				worker := <-wp.WorkerQueue 
				worker <- job 
			
			}

分別輸出臨時(shí)變量worker、w.JobChan,代碼如下:

// 循環(huán)獲取可用的worker,往worker中寫job
	go func() {
		for {
			select {
			//將JobQueue中的數(shù)據(jù)存入WorkerQueue
			case job := <-wp.JobQueue: //線程池中有需要待處理的任務(wù)(數(shù)據(jù)來自于請(qǐng)求的任務(wù)) :讀取JobQueue中的內(nèi)容
				worker := <-wp.WorkerQueue //隊(duì)列中有空閑的Go程   :讀取WorkerQueue中的內(nèi)容,類型為:JobQueue
				worker <- job //空閑的Go程執(zhí)行任務(wù)  :整個(gè)job入隊(duì)列(channel) 類型為:傳遞的參數(shù)(Score結(jié)構(gòu)體)
				fmt.Println("臨時(shí)變量worker:",worker) //todo: 地址是什么
				fmt.Printf("====job類型:%T  ;  worker類型%T======\n",job,worker,)
			}
		}
	}()
//啟動(dòng)參與程序運(yùn)行的Go程數(shù)量
func (w Worker) Run(wq chan JobQueue) {
	go func() {
		for {
		    wq <- w.JobChan //處理任務(wù)的Go程隊(duì)列數(shù)量有限,每運(yùn)行1個(gè),向隊(duì)列中添加1個(gè),隊(duì)列剩余數(shù)量少1個(gè) (JobChain入隊(duì)列)
			select {
			case job := <-w.JobChan:
				fmt.Println("執(zhí)行處理任務(wù)的worker:",w.JobChan) //todo: 地址是什么
				job.Do() //執(zhí)行操作
			}
		}
	}()
}

輸出效果為:

?

結(jié)果發(fā)現(xiàn)worker、w.JobChan是同一地址(指向的地址一樣)。所以 在

worker <- job :向worker中寫數(shù)據(jù);
job := <-w.JobChan: 從w.JobChan中讀數(shù)據(jù)。

由于worker、w.JobChan是對(duì)同一數(shù)據(jù)進(jìn)行操作,所以臨時(shí)變量worker不會(huì)發(fā)生死鎖現(xiàn)象。

說明:也可以將核心內(nèi)容封裝成一個(gè)庫(kù),以后直接調(diào)用即可。

知識(shí)點(diǎn)說明:

1、 地址引用參考:http://m.fzitv.net/jiaoben/353414oen.htm

2、對(duì)應(yīng)channel讀寫的操作(ch為chan 的類型):

ch<-數(shù)據(jù)類型  //數(shù)據(jù)寫入ch

數(shù)據(jù)類型:=<-ch  //ch通道中的數(shù)據(jù)取出,存入數(shù)據(jù)類型對(duì)應(yīng)的變量。

ch1<-ch2 //其中ch1,ch2都是 chan類型(channel類型);執(zhí)行順序是從左至右,將ch2存入ch1(ch2入隊(duì)列ch1),即將ch2看做數(shù)據(jù)添加到通道ch1中。

到此這篇關(guān)于淺談Go語(yǔ)言高并發(fā)處理思路的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 高并發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語(yǔ)言字符串處理庫(kù)strings包詳解

    Go語(yǔ)言字符串處理庫(kù)strings包詳解

    本文詳細(xì)介紹了Go語(yǔ)言中的strings庫(kù)的使用方法,包括字符串的查找、替換、分割、比較、大小寫轉(zhuǎn)換等操作,strings庫(kù)是Go語(yǔ)言中非常重要且功能豐富的標(biāo)準(zhǔn)庫(kù),幾乎涵蓋了所有字符串處理的需求
    2024-09-09
  • 使用Go語(yǔ)言自定義隊(duì)列結(jié)構(gòu)的示例代碼

    使用Go語(yǔ)言自定義隊(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
  • golang根據(jù)URL獲取文件名的示例代碼

    golang根據(jù)URL獲取文件名的示例代碼

    這篇文章主要為大家詳細(xì)介紹了golang根據(jù)URL獲取文件名,文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • Golang中的path/filepath包用法

    Golang中的path/filepath包用法

    這篇文章主要介紹了Golang中的path/filepath包用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語(yǔ)言線程安全之互斥鎖與讀寫鎖

    Go語(yǔ)言線程安全之互斥鎖與讀寫鎖

    這篇文章主要介紹了Go語(yǔ)言線程安全之互斥鎖與讀寫鎖,互斥鎖是為了并發(fā)的安全,在多個(gè)goroutine共同工作的時(shí)候,對(duì)于共享的數(shù)據(jù)十分不安全,而讀寫鎖效率革命,使用鎖的時(shí)候,安全與效率往往需要互相轉(zhuǎn)換,下文詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • go依賴注入庫(kù)samber/do使用示例講解

    go依賴注入庫(kù)samber/do使用示例講解

    這篇文章主要介紹了go依賴注入庫(kù)samber/do使用,在本文中,我們學(xué)習(xí)了如何使用samber/do在?Go?中提供依賴注入,需要的朋友可以參考下
    2024-02-02
  • Golang語(yǔ)言中的Prometheus的日志模塊使用案例代碼編寫

    Golang語(yǔ)言中的Prometheus的日志模塊使用案例代碼編寫

    這篇文章主要介紹了Golang語(yǔ)言中的Prometheus的日志模塊使用案例,本文給大家分享源代碼編寫方法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • golang內(nèi)存逃逸分析

    golang內(nèi)存逃逸分析

    本文主要介紹了golang內(nèi)存逃逸分析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-06-06
  • Go語(yǔ)言的變量定義詳情

    Go語(yǔ)言的變量定義詳情

    這篇文章主要介紹了Go語(yǔ)言的變量定義詳情,go定義變量的方式和c,c++,java語(yǔ)法不一樣,var?變量名類型,var在前,變量名在中間,類型在后面,下文更多詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-03-03
  • Gin框架使用Zap接收日志的實(shí)現(xiàn)

    Gin框架使用Zap接收日志的實(shí)現(xiàn)

    在Gin框架中使用Zap日志中間件替代默認(rèn)日志,通過封裝日志包提升性能,實(shí)現(xiàn)更高效的日志記錄與異?;謴?fù)功能,下面就來介紹一下Gin框架使用Zap接收日志的實(shí)現(xiàn),感興趣的可以了解一下
    2025-05-05

最新評(píng)論

当涂县| 获嘉县| 渭南市| 黔西| 德保县| 台前县| 乌鲁木齐县| 冀州市| 灵川县| 阳江市| 四子王旗| 伊宁市| 德兴市| 朝阳县| 沅江市| 临夏市| 和林格尔县| 威远县| 定日县| 读书| 克东县| 顺平县| 凉城县| 赣榆县| 永年县| 衡阳市| 广安市| 扬州市| 汝南县| 莒南县| 景东| 长岛县| 无极县| 乌兰县| 民丰县| 平邑县| 曲靖市| 泽州县| 林周县| 泗水县| 山西省|