亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go高級特性探究之協(xié)程池詳解

 更新時(shí)間:2023年06月05日 09:13:42   作者:tracy小貓  
在并發(fā)編程中,協(xié)程是?Go?語言的核心特性之一,本文將介紹如何使用?Go?協(xié)程池構(gòu)造一個(gè)協(xié)程池,并解決函數(shù)傳參問題、優(yōu)雅關(guān)閉協(xié)程池和保證協(xié)程安全的問題,感興趣的可以了解一下

在并發(fā)編程中,協(xié)程是 Go 語言的核心特性之一,但是在實(shí)際應(yīng)用中,協(xié)程的創(chuàng)建和銷毀成本比較高。當(dāng)需要同時(shí)處理大量的任務(wù)時(shí),創(chuàng)建大量的協(xié)程會(huì)導(dǎo)致系統(tǒng)開銷變大,進(jìn)而影響程序的性能。這時(shí)候,就需要使用協(xié)程池來管理協(xié)程的生命周期,將協(xié)程的創(chuàng)建和銷毀成本降至最小,提高程序的并發(fā)性能。

本文將介紹如何使用 Go 協(xié)程池構(gòu)造一個(gè)協(xié)程池,并解決函數(shù)傳參問題、優(yōu)雅關(guān)閉協(xié)程池和保證協(xié)程安全的問題。

Pool

type Pool struct {
   capacity       uint64            // 最大協(xié)程數(shù)
   runningWorkers uint64            // 當(dāng)前正在運(yùn)行的協(xié)程數(shù)
   status         int64             // 協(xié)程池的狀態(tài)
   chTask         chan *Task        // 執(zhí)行任務(wù)的 channel
   PanicHandler   func(interface{}) // 處理協(xié)程中的 panic 異常
   sync.Once
   sync.Mutex
}

Pool 類型是協(xié)程池的主要類型,包含了以下屬性:

  • capacity:最大協(xié)程數(shù)。
  • runningWorkers:當(dāng)前正在運(yùn)行的協(xié)程數(shù)。
  • status:協(xié)程池的狀態(tài)。
  • chTask:執(zhí)行任務(wù)的 channel。
  • PanicHandler:處理協(xié)程中的 panic 異常。
  • sync.Once:防止 Stop 函數(shù)被多次調(diào)用。
  • sync.Mutex: 用于鎖定協(xié)程池的狀態(tài)和 channel。

同時(shí) Pool 類型包含以下函數(shù):

  • NewPool:用于初始化協(xié)程池。
  • Submit:將任務(wù)放到 channel 中供協(xié)程進(jìn)行任務(wù)處理。
  • createWorker:用于創(chuàng)建并啟動(dòng)一個(gè)協(xié)程來執(zhí)行任務(wù)。
  • incRunning:增加協(xié)程池的運(yùn)行協(xié)程數(shù)。
  • decRunning:減少協(xié)程池的運(yùn)行協(xié)程數(shù)。
  • Stop:關(guān)閉協(xié)程池,停止接受任務(wù)并且等待所有任務(wù)執(zhí)行完畢后關(guān)閉協(xié)程池。

NewPool 函數(shù)

NewPool 函數(shù)用于創(chuàng)建和初始化一個(gè)協(xié)程池。將最大協(xié)程數(shù) n 和處理協(xié)程中 panic 異常的函數(shù) panicHandler 傳入函數(shù)中,創(chuàng)建一個(gè) Pool 類型,并將屬性初始化后返回一個(gè) Pool 的指針類型。

func NewPool(n uint64, panicHandler func(interface{})) *Pool {
   return &Pool{
      capacity:     n,
      status:       Running,
      chTask:       make(chan *Task, n),
      PanicHandler: panicHandler,
   }
}

Submit 函數(shù)

Submit 函數(shù)用于將任務(wù)放到 channel 中供協(xié)程進(jìn)行任務(wù)處理。首先判斷協(xié)程池狀態(tài)是否為 Stopped,如果已經(jīng)關(guān)閉,則返回一個(gè)錯(cuò)誤;接著加鎖,并判斷 channel 中是否已滿,如果已經(jīng)滿了,則返回一個(gè)錯(cuò)誤,否則將任務(wù)放到 channel 中并返回 nil。

// 將任務(wù)放到 channel 中供協(xié)程進(jìn)行任務(wù)處理
func (p *Pool) Submit(t *Task) error {
   if p.status == Stopped {
      return errors.New("協(xié)程池已關(guān)閉,不能提交任務(wù)")
   }
   p.Lock()
   defer p.Unlock()
   if len(p.chTask) == int(p.capacity) {
      return errors.New("協(xié)程池已滿,不能接受新任務(wù)")
   }
   p.chTask <- t
   return nil
}

createWorker 函數(shù)

createWorker 函數(shù)用于創(chuàng)建并啟動(dòng)一個(gè)協(xié)程來執(zhí)行任務(wù)。首先增加當(dāng)前運(yùn)行的協(xié)程數(shù),然后在一個(gè) go 協(xié)程內(nèi)執(zhí)行任務(wù)。如果在執(zhí)行任務(wù)的過程中出現(xiàn) panic 異常,則調(diào)用 PanicHandler 處理函數(shù),如果沒有設(shè)置 PanicHandler 處理函數(shù),則直接將異常信息打印出來。執(zhí)行完任務(wù)后,減少當(dāng)前運(yùn)行的協(xié)程數(shù)。

// 初始化協(xié)程池的協(xié)程數(shù)量
func (p *Pool) createWorker() {
   p.incRunning()
   // 每一個(gè)協(xié)程獲取一個(gè)任務(wù),執(zhí)行任務(wù)
   go func() {
      defer func() {
         if r := recover(); r != nil {
            if p.PanicHandler != nil {
               p.PanicHandler(r)
            } else {
               fmt.Println("Panic:", r)
            }
         }
         p.decRunning()
      }()
      for {
         select {
         case t := <-p.chTask:
            if t == nil {
               return
            }
            t.Handler(t.Params...)
         }
      }
   }()
}

incRunning、decRunning 函數(shù)

incRunning、decRunning 函數(shù)用于增加和減少協(xié)程池的運(yùn)行協(xié)程數(shù),使用了 atomic.AddUint64 函數(shù)來保證操作的原子性。

// 增加協(xié)程池的運(yùn)行協(xié)程數(shù)
func (p *Pool) incRunning() {
   atomic.AddUint64(&p.runningWorkers, 1)
}
// 減少協(xié)程池的運(yùn)行協(xié)程數(shù)
func (p *Pool) decRunning() {
   atomic.AddUint64(&p.runningWorkers, ^uint64(0))
}

Stop 函數(shù)

Stop 函數(shù)用于關(guān)閉協(xié)程池,停止接受任務(wù)并且等待所有任務(wù)執(zhí)行完畢后關(guān)閉協(xié)程池。首先判斷協(xié)程池狀態(tài)是否為 Running,如果已經(jīng)關(guān)閉,則直接返回;接著將協(xié)程池狀態(tài)設(shè)置為 Stopped,然后使用 sync.Once 確保關(guān)閉 channel 的操作僅被執(zhí)行一次,同時(shí)創(chuàng)建運(yùn)行的協(xié)程數(shù)個(gè)協(xié)程,等待它們執(zhí)行完畢后關(guān)閉協(xié)程池。

// 關(guān)閉協(xié)程池
func (p *Pool) Stop() {
   if p.status == Running {
      p.status = Stopped
      p.Once.Do(func() {
         close(p.chTask)
         for i := uint64(0); i < p.runningWorkers; i++ {
            p.createWorker()
         }
      })
   }
}

解決函數(shù)傳參問題

在使用協(xié)程池時(shí),需要向協(xié)程池提交任務(wù),但是協(xié)程池內(nèi)部的協(xié)程如何知道要執(zhí)行什么樣的任務(wù),參數(shù)又應(yīng)該如何傳遞呢?

為了解決這個(gè)問題,可以定義一個(gè) Task 結(jié)構(gòu)體,用于存儲(chǔ)要執(zhí)行的函數(shù)和函數(shù)參數(shù),如下所示:

type Task struct {
? ? Handler func(v ...interface{})
? ? Params? []interface{}
}

Task 類型是一個(gè)結(jié)構(gòu)體,用于封裝協(xié)程池的任務(wù)。其中 Handler 是一個(gè)函數(shù)類型,用于任務(wù)執(zhí)行的函數(shù);Params 是一個(gè)可變參數(shù),調(diào)用 Handler 時(shí)傳遞給它的參數(shù)。

其中,Handler 是一個(gè)無返回值的函數(shù),且該函數(shù)可接受變長參數(shù),Params 是一個(gè)任意類型的切片,用于傳遞函數(shù)的參數(shù)列表。

在向協(xié)程池提交任務(wù)時(shí),可以將 Task 對象作為參數(shù)進(jìn)行提交。

pool.Submit(&Task{
? ? Handler: func(v ...interface{}) {
? ? ? ? // 執(zhí)行任務(wù)的代碼
? ? },
? ? Params: []interface{}{...}, // 任務(wù)的參數(shù)列表
})

在協(xié)程內(nèi)部,可以通過調(diào)用 Task.Handler 方法,并將 Task.Params 作為參數(shù)傳遞進(jìn)去,來運(yùn)行具體的任務(wù)。

select {
case t := <-p.chTask:
? ? if t == nil {
? ? ? ? return
? ? }
? ? t.Handler(t.Params...)
}

通過這種方式,協(xié)程池就能夠動(dòng)態(tài)地執(zhí)行不同的任務(wù),并且傳遞任意類型和數(shù)量的參數(shù)。

優(yōu)雅關(guān)閉協(xié)程池

在使用協(xié)程池時(shí),如何正確地關(guān)閉協(xié)程池,以避免因未正確關(guān)閉而導(dǎo)致的內(nèi)存泄漏和程序崩潰呢?

首先,需要明確協(xié)程池的運(yùn)行狀態(tài),通過內(nèi)部的 status 參數(shù)控制協(xié)程池的開關(guān)。當(dāng)協(xié)程池處于運(yùn)行狀態(tài)時(shí),協(xié)程池才能夠接受新的任務(wù),否則應(yīng)該拒絕新的任務(wù)請求,并盡快釋放內(nèi)部的資源。

其次,在關(guān)閉協(xié)程池時(shí),需要確保所有的已運(yùn)行的協(xié)程都已經(jīng)執(zhí)行完任務(wù)并退出。這時(shí),可以使用 sync.Once 來執(zhí)行一次協(xié)程池的清理工作。當(dāng)協(xié)程池處于關(guān)閉狀態(tài)時(shí),不再接受新的任務(wù),并通知所有的協(xié)程退出任務(wù)循環(huán),最終實(shí)現(xiàn)協(xié)程池的優(yōu)雅關(guān)閉。

func (p *Pool) Stop() {
?if p.status == Running {
? p.status = Stopped
? p.Once.Do(func() {
?? close(p.chTask)
?? for i := uint64(0); i < p.runningWorkers; i++ {
? ? p.createWorker()
?? }
? })
?}
}

保證協(xié)程安全

在使用協(xié)程池時(shí),需要注意線程安全問題,尤其是在多個(gè)協(xié)程同時(shí)訪問協(xié)程池時(shí),需要保證協(xié)程池的內(nèi)部狀態(tài)是線程安全的。

同時(shí)對于狀態(tài)的變更以及數(shù)量的增減,還需要保證代碼的安全性。

為了保證線程安全,可以使用互斥鎖 sync.Mutex 來鎖定協(xié)程池,以避免多個(gè)協(xié)程同時(shí)讀寫協(xié)程池的運(yùn)行狀態(tài)和其他內(nèi)部參數(shù)。

在協(xié)程池的內(nèi)部實(shí)現(xiàn)中,使用的 sync.Once 只會(huì)單次執(zhí)行的特性可以保證協(xié)程池只會(huì)初始化一次,防止因多次初始化而導(dǎo)致的內(nèi)存泄漏或其他異常。

測試用例

為了測試協(xié)程池的正確性,以下是一個(gè)簡單的測試用例。該測試用例創(chuàng)建一個(gè)容量為 3 的協(xié)程池,并向其中提交 10 個(gè)任務(wù),每個(gè)任務(wù)隨機(jī)睡眠一段時(shí)間,并輸出當(dāng)前時(shí)間。

package main
import (
?"fmt"
?"math/rand"
?"sync"
?"testing"
?"time"
)
func TestPool(t *testing.T) {
?pool := NewPool(3, func(err interface{}) {
? fmt.Println("發(fā)生 panic,錯(cuò)誤信息:", err)
?})
?var wg sync.WaitGroup
?for i := 0; i < 10; i++ {
? wg.Add(1)
? go func(id int) {
?? defer wg.Done()
?? task := \&Task{
? ? Handler: func(v ...interface{}) {
?? ? fmt.Printf("任務(wù) %d 開始執(zhí)行,時(shí)間:%v\n", id, time.Now().Format("2006-01-02 15:04:05"))
?? ? rand.Seed(time.Now().UnixNano())
?? ? time.Sleep(time.Duration(rand.Intn(5)) \* time.Second)
?? ? fmt.Printf("任務(wù) %d 執(zhí)行完畢,時(shí)間:%v\n", id, time.Now().Format("2006-01-02 15:04:05"))
? ? },
? ? Params: \[]interface{}{},
?? }
?? pool.Submit(task)
? }(i)
?}
?wg.Wait()
}

輸出結(jié)果如下:

任務(wù) 0 開始執(zhí)行,時(shí)間:2021-10-05 16:52:22
任務(wù) 1 開始執(zhí)行,時(shí)間:2021-10-05 16:52:22
任務(wù) 2 開始執(zhí)行,時(shí)間:2021-10-05 16:52:22
任務(wù) 0 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:27
任務(wù) 3 開始執(zhí)行,時(shí)間:2021-10-05 16:52:27
任務(wù) 4 開始執(zhí)行,時(shí)間:2021-10-05 16:52:27
任務(wù) 1 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:28
任務(wù) 5 開始執(zhí)行,時(shí)間:2021-10-05 16:52:28
任務(wù) 6 開始執(zhí)行,時(shí)間:2021-10-05 16:52:28
任務(wù) 7 開始執(zhí)行,時(shí)間:2021-10-05 16:52:28
任務(wù) 4 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:29
任務(wù) 8 開始執(zhí)行,時(shí)間:2021-10-05 16:52:29
任務(wù) 9 開始執(zhí)行,時(shí)間:2021-10-05 16:52:29
任務(wù) 2 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:32
任務(wù) 5 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:33
任務(wù) 7 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:33
任務(wù) 6 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:34
任務(wù) 3 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:35
任務(wù) 9 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:35
任務(wù) 8 執(zhí)行完畢,時(shí)間:2021-10-05 16:52:37

從輸出結(jié)果可以看出,協(xié)程池成功并行處理了所有的任務(wù),并且在容量限制的情況下,成功地保證了協(xié)程池的線程安全性。

改進(jìn)

可考慮增加對協(xié)程池容量的動(dòng)態(tài)調(diào)整算法,例如在高峰期時(shí)增加協(xié)程池的容量,低谷期時(shí)降低協(xié)程池的容量。另外可以增加協(xié)程池的超時(shí)控制機(jī)制,以避免任務(wù)執(zhí)行時(shí)間過長導(dǎo)致系統(tǒng)資源浪費(fèi)和性能下降。

總結(jié)

協(xié)程池是 Go 語言中一種重要的并發(fā)編程模式,通過協(xié)程池可以高效地管理協(xié)程的生命周期、避免協(xié)程的頻繁創(chuàng)建和銷毀,提高程序的并發(fā)性能。在使用協(xié)程池時(shí),需要注意解決函數(shù)傳參問題、優(yōu)雅關(guān)閉協(xié)程池和保證協(xié)程安全的問題,通過合理使用互斥鎖和 sync.Once 可以有效解決這些問題,從而保證協(xié)程池的正確性和高效性。

以上就是Go高級特性探究之協(xié)程池詳解的詳細(xì)內(nèi)容,更多關(guān)于Go協(xié)程池的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang sql語句超時(shí)控制方案及原理

    golang sql語句超時(shí)控制方案及原理

    一般應(yīng)用程序在執(zhí)行一條sql語句時(shí),都會(huì)給這條sql設(shè)置一個(gè)超時(shí)時(shí)間,本文主要介紹了golang sql語句超時(shí)控制方案及原理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • go內(nèi)置函數(shù)copy()的具體使用

    go內(nèi)置函數(shù)copy()的具體使用

    當(dāng)我們在Go語言中需要將一個(gè)切片的內(nèi)容復(fù)制到另一個(gè)切片時(shí),可以使用內(nèi)置的copy()函數(shù),本文就介紹了go內(nèi)置函數(shù)copy()的具體使用,感興趣的可以了解一下
    2023-08-08
  • Go語言循環(huán)遍歷含有中文的字符串的方法小結(jié)

    Go語言循環(huán)遍歷含有中文的字符串的方法小結(jié)

    這篇文章主要介紹了Go語言循環(huán)遍歷含有中文的字符串的幾種方法,文章通過代碼示例講解的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • Golang正整數(shù)指定規(guī)則排序算法問題分析

    Golang正整數(shù)指定規(guī)則排序算法問題分析

    這篇文章主要介紹了Golang正整數(shù)指定規(guī)則排序算法問題,結(jié)合實(shí)例形式分析了Go語言排序算法操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2017-01-01
  • Golang常見錯(cuò)誤之值拷貝和for循環(huán)中的單一變量詳解

    Golang常見錯(cuò)誤之值拷貝和for循環(huán)中的單一變量詳解

    這篇文章主要給大家介紹了關(guān)于Golang常見錯(cuò)誤之值拷貝和for循環(huán)中單一變量的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • 一文帶你了解Go語言中的類型斷言和類型轉(zhuǎn)換

    一文帶你了解Go語言中的類型斷言和類型轉(zhuǎn)換

    在Go中,類型斷言和類型轉(zhuǎn)換是一個(gè)令人困惑的事情,他們似乎都在做同樣的事情。最明顯的不同點(diǎn)是他們具有不同的語法(variable.(type)?vs?type(variable)?)。本文我們就來深入研究一下二者的區(qū)別
    2022-09-09
  • 源碼解析gtoken替換jwt實(shí)現(xiàn)sso登錄

    源碼解析gtoken替換jwt實(shí)現(xiàn)sso登錄

    這篇文章主要為大家介紹了源碼解析gtoken替換jwt實(shí)現(xiàn)sso登錄的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go標(biāo)準(zhǔn)庫http?server優(yōu)雅啟動(dòng)深入理解

    Go標(biāo)準(zhǔn)庫http?server優(yōu)雅啟動(dòng)深入理解

    這篇文章主要介紹了Go標(biāo)準(zhǔn)庫http?server優(yōu)雅啟動(dòng)深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Golang爬蟲框架colly使用淺析

    Golang爬蟲框架colly使用淺析

    這篇文章主要介紹了Golang爬蟲框架colly的使用,colly是Go實(shí)現(xiàn)的比較有名的一款爬蟲框架,而且Go在高并發(fā)和分布式場景的優(yōu)勢也正是爬蟲技術(shù)所需要的,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05
  • Go生成base64圖片驗(yàn)證碼實(shí)例(超詳細(xì)工具類)

    Go生成base64圖片驗(yàn)證碼實(shí)例(超詳細(xì)工具類)

    這段時(shí)間需要使用圖片驗(yàn)證碼庫,下面這篇文章主要給大家介紹了關(guān)于Go生成base64圖片驗(yàn)證碼的相關(guān)資料,文中給出了詳細(xì)的實(shí)例代碼,需要的朋友可以參考下
    2023-06-06

最新評論