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

Golang工作池的使用實例講解

 更新時間:2023年02月20日 09:52:05   作者:尋找09之夏  
我們使用Go語言開發(fā)項目,常常會使用到goroutine;goroutine太多會造成系統(tǒng)占用過高或其他系統(tǒng)異常,我們可以將goroutine控制指定數(shù)量,且減少goroutine的創(chuàng)建,這就運用到Go工作池,下面就介紹和使用一下

一、概念

我們可以將工作池理解為線程池。線程池的創(chuàng)建和銷毀非常消耗資源,所以專門寫一個pool,每次用過的線程池再放回pool中而不是銷毀。不過在Go語言中不會使用系統(tǒng)的線程,而是使用goroutine。gorotine的創(chuàng)建和銷毀比系統(tǒng)線程的消耗要小的多,而且goroutine沒有標(biāo)號。所以goroutine的pool就不再時線程池,而是work pool(工作池)。

雖然goroutine的系統(tǒng)消耗較小,但也不能隨意在編碼時使用go func(),如果程序頻繁啟動goroutine,會造成極其不可控性能問題。對于可以提前預(yù)知的大量異步處理的任務(wù)就要考慮使用工作池。

工作池的作用控制goroutine的規(guī)模,或者說是goroutine的數(shù)量。在Go語言中,控制goroutine的數(shù)量最好方式就是使用緩存通道。

二、實例

1.簡單示例

下面是Go語言解決工作池的經(jīng)典用法。

func worker(id int, jobs <-chan int, results chan<- int) {
	for job := range jobs {
		fmt.Printf("worker(%d) start to do job(%d)\n", id, job)
		time.Sleep(time.Second)
		fmt.Printf("worker(%d) finished job(%d)\n", id, job)
		results <- job
	}
}
func main() {
    // 為了使用我們的工作池,我們需要發(fā)送工作和接受工作的結(jié)果,
    // 這里我們定義兩個通道,一個jobs,一個results
	jobs := make(chan int, 100)
	results := make(chan int, 100)
	// 開啟3個goroutine
	for id := 1; id <= 3; id++ {
		go worker(id, jobs, results)
	}
	// 創(chuàng)建5個任務(wù)
	for job := 1; job <= 5; job++ {
		jobs <- job
	}
	close(jobs)
	// 輸出結(jié)果
	for i := 1; i <= 5; i++ {
		<-results
	}
}

上述代碼工作池思想主要體現(xiàn)在jobs的通道上,因為定義了一個緩存長度為100的通道,所以在通道到100以后,新任務(wù)就會阻塞,只有等worker從通道取走一個工作以后才能繼續(xù)分配新工作。

本案例較為簡單,如果worker的數(shù)量較大,業(yè)務(wù)執(zhí)行時間較長的話,我們需要在程序設(shè)計上將jobs和worker的模式進行優(yōu)化,每個worker處理一項工作,工作池可以自定義最大數(shù)量的worker;這樣可以保證goroutine的最大數(shù)量,可程序更加可控,避免代碼消耗壓垮系統(tǒng)。

2.讀入數(shù)據(jù)

下面時改良之后代碼

1package main
import (
	"fmt"
	"reflect"
	"time"
)
// Job 任務(wù)內(nèi)容
type Job struct {
	ID   int
	Name string
}
// Worker 工作
type Worker struct {
	id         int           // id
	WorkerPool chan chan Job // 工作者池(通道的通道),每個元素都是一個job通道, 公共的job
	JobChannel chan Job      // 工作通道,每個元素是一個job,worker私有的job
	exit       chan bool     // 結(jié)束信號
}
var (
	MaxWorker = 5                 // 最大worker數(shù)量
	JobQueue  = make(chan Job, 5) // 工作通道,模擬需處理的工作
)
// Scheduler 排程中心
type Scheduler struct {
	WorkerPool   chan chan Job // 工作池
	WorkerMaxNum int           // 最大工作者數(shù)
	Workers      []*Worker     // worker隊列
}
// NewScheduler 創(chuàng)建排程中心
func NewScheduler(workerMaxNum int) *Scheduler {
	workerPool := make(chan chan Job, workerMaxNum) // 工作池
	return &Scheduler{WorkerPool: workerPool, WorkerMaxNum: workerMaxNum}
}
// Start 工作池開始
func (s *Scheduler) Start() {
	Workers := make([]*Worker, s.WorkerMaxNum)
	for i := 0; i < s.WorkerMaxNum; i++ {
		worker := NewWorker(s.WorkerPool, i)
		worker.Start()
		Workers[i] = &worker
	}
	s.Workers = Workers
	go s.schedule()
}
// Stop 工作池的關(guān)閉
func (s *Scheduler) Stop() {
	Workers := s.Workers
	for _, w := range Workers {
		w.Stop()
	}
	time.Sleep(time.Second)
	close(s.WorkerPool)
}
func NewWorker(WorkerPool chan chan Job, id int) Worker {
	fmt.Printf("new a worker(%d)\n", id)
	return Worker{
		id:         id,
		WorkerPool: WorkerPool,
		JobChannel: make(chan Job),
		exit:       make(chan bool),
	}
}
// Start 監(jiān)聽任務(wù)和結(jié)束信號
func (w Worker) Start() {
	go func() {
		for {
			select {
			case job := <-w.JobChannel: // 收到任務(wù)
				fmt.Println("get a job from private w.JobChannel")
				fmt.Println(job)
			case <-w.exit: // 收到結(jié)束信號
				fmt.Println("worker exit", w)
				return
			}
		}
	}()
}
func (w Worker) Stop() {
	go func() {
		w.exit <- true
	}()
}
// 排程
func (s *Scheduler) schedule() {
	for {
		select {
		case job := <-JobQueue:
			fmt.Println("get a job from JobQueue")
			go func(job Job) {
				//從WorkerPool獲取jobChannel,忙時阻塞
				jobChannel := <-s.WorkerPool
				fmt.Println("get a private jobChannel from public s.WorkerPool", reflect.TypeOf(jobChannel))
				jobChannel <- job
				fmt.Println("worker's private jobChannel add one job")
			}(job)
		}
	}
}
func main() {
	scheduler := NewScheduler(MaxWorker)
	scheduler.Start()
	jobQueue()
	scheduler.Stop()
}
// 模擬Job任務(wù)
func jobQueue() {
	for i := 1; i <= 30; i++ {
		JobQueue <- Job{ID: i, Name: fmt.Sprintf("Job【%d】", i)}
		fmt.Printf("jobQueue add %d job\n", i)
	}
}

定義了兩個結(jié)構(gòu)體:Task任務(wù)和Job工作,Task并沒有實質(zhì)性的內(nèi)容,這里僅僅定義了一個整型變量;

定義兩個全局變量:MaxWorker是最大的worker數(shù)量;JobQueue是Job的通道。這兩個變量都用于后面的模擬,在真實場景中可以不設(shè)置這兩個變量。

定義了一個Worker結(jié)構(gòu)體,與上一個簡單工作池的示例不同,本例的Worker不再是簡單的一個goroutine,而是一個結(jié)構(gòu)體。結(jié)構(gòu)體內(nèi)定義了如下四個變量。?id:worker編號。?exit:這是一個bool類型的通道,當(dāng)有數(shù)據(jù)寫入時worker結(jié)束運行。?JobChannel:Job類型的通道,該通道是專屬于當(dāng)前worker的私有工作隊列。?WorkerPool:注意看,定義的時候使用了兩個Channel,每一個元素是一個Job通道,其實每一個元素是一個JobChannel。

NewWorker方法用于創(chuàng)建一個新的worker,要注意該方法的參數(shù)workerPool用于創(chuàng)建worker時傳入,這就說明每個worker與其他worker的WorkerPool是共享的,或者說多個worker使用一個WorkerPool。這一點很重要,這是本示例代碼在上一個簡單示例代碼基礎(chǔ)上的優(yōu)化。而JobChannel和exit變量則是隨著Worker的新建而新建的。

Worker的Start方法,該方法用于監(jiān)聽任務(wù)或者結(jié)束信號。Start方法一開始就用goroutine運行一個匿名函數(shù),而函數(shù)內(nèi)部是一個無限循環(huán)。在循環(huán)內(nèi)部,首先是把當(dāng)前的JobChannel注冊到WorkerPool里,一旦注冊進去也就說明該worker可以接收任務(wù)了。然后通過select判斷JobChannel是否可以讀取,也就是其中是否有Job,或者exit通道是否可以讀取。如果JobChannel可讀取,證明有Job,后續(xù)開始處理Job;而如果exit可讀,則結(jié)束當(dāng)前的無限循環(huán)。所以,后面的代碼中要特別注意對WorkerPool的操作,Worker是從WorkerPool領(lǐng)取工作的。Worker的Stop方法,用于為exit通道寫入數(shù)據(jù),在Start方法內(nèi)Worker會讀取到寫入的數(shù)據(jù),進而結(jié)束無限循環(huán)。

NewScheduler函數(shù)用于創(chuàng)建一個Scheduler,可以看到函數(shù)內(nèi)部的WorkerPool是通過make函數(shù)新建的,NewWorker函數(shù)一樣靠參數(shù)傳入。注意WorkerPool是有緩存通道的,緩存長度是MaxWorkers。

Scheduler的Create方法,該方法根據(jù)MaxWorkers最大數(shù)創(chuàng)建Worker,并且把引用存入Workers切片。創(chuàng)建好Worker后,馬上調(diào)用Worker的Start方法,最后通過goroutine運行Schedule方法。Scheduler的Shutdown方法,用于關(guān)閉工作池,調(diào)用所有worker的Stop方法并且關(guān)閉WorkerPool工作池。

Scheduler的Schedule方法,該方法內(nèi)也是一個無限循環(huán),循環(huán)內(nèi)部就是不停地讀取JobQueue,然后運行一個goroutine。在新運行的goroutine內(nèi)從s.WorkerPool讀取一個JobChannel,注意,Worker注冊到WorkerPool以后此處才可以讀取到,如果WorkerPool的緩存通道內(nèi)沒有JobChannel,則會阻塞,直到讀取到JobChannel,才把Job寫入。

備注:此文內(nèi)容來自《Go微服務(wù)實戰(zhàn)》

到此這篇關(guān)于Golang工作池的使用實例講解的文章就介紹到這了,更多相關(guān)Go工作池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言中的UTF-8實現(xiàn)

    Go語言中的UTF-8實現(xiàn)

    這篇文章主要介紹了Go語言中的UTF-8實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Go語言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實現(xiàn)方法

    Go語言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實現(xiàn)方法

    對音視頻一直是小白,決定沉下心來,好好研究一下音視頻知識,下面這篇文章主要給大家介紹了關(guān)于Go語言學(xué)習(xí)之將mp4通過rtmp推送流媒體服務(wù)的實現(xiàn)方法,需要的朋友可以參考下
    2022-12-12
  • go語言Timer計時器的用法示例詳解

    go語言Timer計時器的用法示例詳解

    Go語言的標(biāo)準(zhǔn)庫里提供兩種類型的計時器Timer和Ticker。這篇文章通過實例代碼給大家介紹go語言Timer計時器的用法,代碼簡單易懂,感興趣的朋友跟隨小編一起看看吧
    2020-05-05
  • Sublime Text3安裝Go語言相關(guān)插件gosublime時搜不到gosublime的解決方法

    Sublime Text3安裝Go語言相關(guān)插件gosublime時搜不到gosublime的解決方法

    本文主要介紹了Sublime Text3安裝Go語言相關(guān)插件gosublime時搜不到gosublime的解決方法,具有一定的參考價值,感興趣的可以了解一下
    2022-01-01
  • Go實現(xiàn)set類型的示例代碼

    Go實現(xiàn)set類型的示例代碼

    本文主要介紹了Go實現(xiàn)set類型的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Go Mongox輕松實現(xiàn)MongoDB的時間字段自動填充

    Go Mongox輕松實現(xiàn)MongoDB的時間字段自動填充

    這篇文章主要為大家詳細(xì)介紹了Go語言如何使用 mongox 庫,在插入和更新數(shù)據(jù)時自動填充時間字段,從而提升開發(fā)效率并減少重復(fù)代碼,需要的可以參考下
    2025-02-02
  • Go中的字典Map增刪改查、排序及其值類型

    Go中的字典Map增刪改查、排序及其值類型

    本文詳細(xì)介紹了Go語言中Map的基本概念、聲明初始化、增刪改查操作、反轉(zhuǎn)、排序以及如何判斷鍵是否存在等操作,Map是一種基于鍵值對的無序數(shù)據(jù)結(jié)構(gòu),鍵必須是支持相等運算符的類型,值可以是任意類型,初始化Map時推薦指定容量以提高性能
    2024-09-09
  • golang-gorm自動建表問題

    golang-gorm自動建表問題

    這篇文章主要介紹了golang-gorm自動建表問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Go基礎(chǔ)教程之環(huán)境搭建及常用命令

    Go基礎(chǔ)教程之環(huán)境搭建及常用命令

    這篇文章主要介紹了Go基礎(chǔ)教程之環(huán)境搭建及常用命令的相關(guān)資料,包括Go語言簡介、環(huán)境配置、包管理工具GoModules以及常用命令的全面介紹,需要的朋友可以參考下
    2025-03-03
  • 詳解Opentelemetry Collector采集器

    詳解Opentelemetry Collector采集器

    這篇文章主要為大家介紹了Opentelemetry Collector神秘的采集器詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論