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

go語言限制協(xié)程并發(fā)數(shù)的方案詳情

 更新時(shí)間:2022年01月24日 14:39:03   作者:DoubleLi  
一個(gè)線程中可以有任意多個(gè)協(xié)程,但某一時(shí)刻只能有一個(gè)協(xié)程在運(yùn)行,多個(gè)協(xié)程分享該線程分配到的計(jì)算機(jī)資源,接下來通過本文給大家介紹go語言限制協(xié)程的并發(fā)數(shù)的方案詳情,感興趣的朋友一起看看吧

前言

在使用協(xié)程并發(fā)處理某些任務(wù)時(shí), 其并發(fā)數(shù)量往往因?yàn)楦鞣N因素的限制不能無限的增大. 例如網(wǎng)絡(luò)請求、數(shù)據(jù)庫查詢等等。從運(yùn)行效率角度考慮,在相關(guān)服務(wù)可以負(fù)載的前提下(限制最大并發(fā)數(shù)),盡可能高的并發(fā)。本文就這個(gè)問題探尋一下解決方案和實(shí)現(xiàn)。共兩種思路,一是使用帶緩沖的通道實(shí)現(xiàn),二是使用鎖實(shí)現(xiàn)。

一、使用帶緩沖的通道限制并發(fā)數(shù)

1.1方案詳情

先上代碼如下, 邏輯很簡單.

package golimit

type GoLimit struct {
    ch chan int
}
func NewGoLimit(max int) *GoLimit {
    return &GoLimit{ch: make(chan int, max)}
func (g *GoLimit) Add() {
    g.ch <- 1
func (g *GoLimit) Done() {
    <-g.ch

按允許最大并發(fā)數(shù)創(chuàng)建一個(gè)帶緩沖的通道, 創(chuàng)建協(xié)程之前調(diào)用Add()往通道里寫一個(gè)數(shù)據(jù), 協(xié)程完成是調(diào)用Done()方法讀取一個(gè)數(shù)據(jù). 若無法往通道里寫數(shù)據(jù)時(shí), 表示通道已經(jīng)寫滿, 也就是目前的協(xié)程并發(fā)數(shù)為允許的最大數(shù)量. Add()方法將被阻塞, 也就無法創(chuàng)建新的協(xié)程. 直到有協(xié)程運(yùn)行完成, 調(diào)用Done()方法讀取了通道了一個(gè)數(shù)據(jù).

以下是使用示例

package main

import (
    "golimit"
    "log"
    "time"
)
func main() {
    log.Println("開始測試...")
    g := golimit.NewGoLimit(2) //max_num(最大允許并發(fā)數(shù))設(shè)置為2
    for i := 0; i < 10; i++ {
        //嘗試增加一個(gè)協(xié)程, 若已達(dá)到最大并發(fā)數(shù),將阻塞
        g.Add()
        go func(g *golimit.GoLimit, i int) {
            defer g.Done() //一個(gè)并發(fā)協(xié)程已經(jīng)完成
            time.Sleep(time.Second * 2)
            log.Println(i, "done")
        }(g, i)
    }
    log.Println("循環(huán)結(jié)束")
    time.Sleep(time.Second * 3)//等待執(zhí)行完成
    log.Println("測試結(jié)束")
}

1.2評估總結(jié)

優(yōu)點(diǎn):此方案的實(shí)現(xiàn)邏輯簡單明了,易理解、易維護(hù)。若能滿足需求,在一般的場景下,此方案為首選。

隱憂:使用通道的緩沖區(qū)的大小來表示最大可并發(fā)數(shù),在允許并發(fā)數(shù)較大,如幾千幾萬甚至更大的情況下,通道的性能和內(nèi)存的負(fù)載是否會(huì)有問題,我不太清楚,若哪位朋友知道請告知一下。

不足:運(yùn)行中難以調(diào)整最大可并發(fā)數(shù)。而在某些場景下是有這種需求的,如A服務(wù)依賴的B服務(wù)有擴(kuò)容或縮減,但A服務(wù)不能停止,需要調(diào)整請求B服務(wù)接口的最大可并發(fā)數(shù)。二、使用鎖實(shí)現(xiàn)協(xié)程并發(fā)數(shù)量限制2.1方案詳情

同樣先上代碼(注:此代碼我已經(jīng)在github上開源https://github.com/zh-five/golimit

// 協(xié)程并發(fā)數(shù)限制庫
package golimit
import (
    "sync"
)
type GoLimit struct {
    max       uint             //并發(fā)最大數(shù)量
    count     uint             //當(dāng)前已有并發(fā)數(shù)
    isAddLock bool             //是否已鎖定增加
    zeroChan  chan interface{} //為0時(shí)廣播
    addLock   sync.Mutex       //(增加并發(fā)數(shù)的)鎖
    dataLock  sync.Mutex       //(修改數(shù)據(jù)的)鎖
}
func NewGoLimit(max uint) *GoLimit {
    return &GoLimit{max: max, count: 0, isAddLock: false, zeroChan: nil}
}
//并發(fā)計(jì)數(shù)加1.若 計(jì)數(shù)>=max_num, 則阻塞,直到 計(jì)數(shù)<max_num
func (g *GoLimit) Add() {
    g.addLock.Lock()
    g.dataLock.Lock()
    g.count += 1
    if g.count < g.max { //未超并發(fā)時(shí)解鎖,后續(xù)可以繼續(xù)增加
        g.addLock.Unlock()
    } else { //已到最大并發(fā)數(shù), 不解鎖并標(biāo)記. 等數(shù)量減少后解鎖
        g.isAddLock = true
    }
    g.dataLock.Unlock()
}
//并發(fā)計(jì)數(shù)減1
//若計(jì)數(shù)<max_num, 可以使原阻塞的Add()快速解除阻塞
func (g *GoLimit) Done() {
    g.dataLock.Lock()
    g.count -= 1
    //解鎖
    if g.isAddLock == true && g.count < g.max {
        g.isAddLock = false
        g.addLock.Unlock()
    }
    //0廣播
    if g.count == 0 && g.zeroChan != nil {
        close(g.zeroChan)
        g.zeroChan = nil
    }
    g.dataLock.Unlock()
}
//更新最大并發(fā)計(jì)數(shù)為, 若是調(diào)大, 可以使原阻塞的Add()快速解除阻塞
func (g *GoLimit) SetMax(n uint) {
    g.dataLock.Lock()
    g.max = n
    //解鎖
    if g.isAddLock == true && g.count < g.max {
        g.isAddLock = false
        g.addLock.Unlock()
    }
    //加鎖
    if g.isAddLock == false && g.count >= g.max {
        g.isAddLock = true
        g.addLock.Lock()
    }
    g.dataLock.Unlock()
}
//若當(dāng)前并發(fā)計(jì)數(shù)為0, 則快速返回; 否則阻塞等待,直到并發(fā)計(jì)數(shù)為0
func (g *GoLimit) WaitZero() {
    g.dataLock.Lock()
    //無需等待
    if g.count == 0 {
        g.dataLock.Unlock()
        return
    }
    //無廣播通道, 創(chuàng)建一個(gè)
    if g.zeroChan == nil {
        g.zeroChan = make(chan interface{})
    }
    //復(fù)制通道后解鎖, 避免從nil讀數(shù)據(jù)
    c := g.zeroChan
    g.dataLock.Unlock()
    <-c
}
//獲取并發(fā)計(jì)數(shù)
func (g *GoLimit) Count() uint {
    return g.count
}
//獲取最大并發(fā)計(jì)數(shù)
func (g *GoLimit) Max() uint {
    return g.max
}

總共使用了兩把鎖,一把是數(shù)據(jù)鎖(dataLock),用來鎖定數(shù)據(jù),保證數(shù)據(jù)修改安全,加鎖解鎖是在修改數(shù)據(jù)前后進(jìn)行的;另一把是增加能否增加協(xié)程的鎖(addLock),增加協(xié)程時(shí)必須先加鎖,加鎖成功后修改并發(fā)數(shù),若并發(fā)數(shù)小于最大可并發(fā)數(shù),則解鎖,否則不解鎖,促使后續(xù)增加協(xié)程的加鎖操作阻塞,從而限制協(xié)程的并發(fā)數(shù)。使用示例如下:

package main
import (
    "github.com/zh-five/golimit"
    "log"
    "time"
)
func main() {
    log.Println("開始測試...")
    g := golimit.NewGoLimit(2) //max_num(最大允許并發(fā)數(shù))設(shè)置為2
    for i := 0; i < 10; i++ {
        //并發(fā)計(jì)數(shù)加1.若 計(jì)數(shù)>=max_num, 則阻塞,直到 計(jì)數(shù)<max_num
        g.Add()
        //運(yùn)行過程中可以隨時(shí)修改最大可并發(fā)數(shù)據(jù)
        //g.SetMax(3)
        go func(g *golimit.GoLimit, i int) {
            defer g.Done() //并發(fā)計(jì)數(shù)減1
            time.Sleep(time.Second * 2)
            log.Println(i, "done")
        }(g, i)
    }
    log.Println("循環(huán)結(jié)束")
    g.WaitZero() //阻塞, 直到所有并發(fā)都完成
    log.Println("測試結(jié)束")
}

方案2的GoLimit除了增加了SetMax()方法用于修改最大可并發(fā)數(shù)。出于好玩和偷懶增加了一個(gè)WaitZero()方法(其實(shí)外部使用sync.WaitGroup也可以快速實(shí)現(xiàn)此功能),用于阻塞等待所有并發(fā)協(xié)程都執(zhí)行完成。大約可以用于如下場景:有一大批url需要有限制的并發(fā)采集數(shù)據(jù),主程序里只需要簡單的調(diào)用一下WaitZero()方法,就可以阻塞等等所有采集的協(xié)程完成。

2.2評估總結(jié)

  • 優(yōu)點(diǎn): 從實(shí)現(xiàn)邏輯上說,可以確定性能和消耗不會(huì)隨著最大可并發(fā)數(shù)增加而線性增加。另外還有很多可擴(kuò)展的想象。
  • 缺點(diǎn):實(shí)現(xiàn)邏輯比較復(fù)雜

其它

其實(shí)我很想對比測試一下兩種方案的性能,特別是最大可并發(fā)比較大時(shí)。但我一直沒有找到一種好的測試方法,若哪個(gè)朋友有方法或思路,歡迎交流。

到此這篇關(guān)于go語言限制協(xié)程并發(fā)數(shù)的方案詳情的文章就介紹到這了,更多相關(guān)go限制協(xié)程并發(fā)數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言{}大括號(hào)的特殊用法實(shí)例探究

    Go語言{}大括號(hào)的特殊用法實(shí)例探究

    這篇文章主要為大家介紹了Go語言{}大括號(hào)的特殊用法實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Go自定義數(shù)據(jù)序列化的流程詳解

    Go自定義數(shù)據(jù)序列化的流程詳解

    在Go語言中,自定義數(shù)據(jù)的序列化是一個(gè)常見的需求,本文將深入探討 Go 語言中自定義數(shù)據(jù)序列化的流程,包括關(guān)鍵概念、技巧和最佳實(shí)踐,旨在幫助開發(fā)者更高效地進(jìn)行數(shù)據(jù)序列化工作,需要的朋友可以參考下
    2024-06-06
  • golang實(shí)現(xiàn)給圖片加水印

    golang實(shí)現(xiàn)給圖片加水印

    這篇文章主要為大家詳細(xì)介紹了Vue3如何利用golang實(shí)現(xiàn)給圖片加水印,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2023-12-12
  • 詳解Golang語言HTTP客戶端實(shí)踐

    詳解Golang語言HTTP客戶端實(shí)踐

    本文主要介紹了Golang語言HTTP客戶端實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • 如何基于Golang實(shí)現(xiàn)Kubernetes邊車模式

    如何基于Golang實(shí)現(xiàn)Kubernetes邊車模式

    本文介紹了如何基于Go實(shí)現(xiàn)Kubernetes Sidecar模式,并通過實(shí)際示例演示創(chuàng)建Golang實(shí)現(xiàn)的微服務(wù)服務(wù)、Docker 容器化以及在 Kubernetes 上的部署和管理,感興趣的朋友一起看看吧
    2024-08-08
  • 從基礎(chǔ)到高級(jí)全方位解析Go中反射的應(yīng)用

    從基礎(chǔ)到高級(jí)全方位解析Go中反射的應(yīng)用

    本文我們將全面深入地探討Go語言的反射機(jī)制,從反射的基礎(chǔ)概念、為什么需要反射,到如何在Go中實(shí)現(xiàn)反射,以及在高級(jí)編程場景如泛型編程和插件架構(gòu)中的應(yīng)用,需要的可以參考下
    2023-10-10
  • gorm整合進(jìn)go-zero的實(shí)現(xiàn)方法

    gorm整合進(jìn)go-zero的實(shí)現(xiàn)方法

    go-zero提供的代碼生成器里面,沒有提供orm框架操作,但是提供了遍歷的緩存操作,所以可以利用gorm當(dāng)作一個(gè)sql語句的生成器,把生成后的sql語句放到go-zero生成的模板中去執(zhí)行,對gorm整合進(jìn)go-zero的實(shí)現(xiàn)方法感興趣的朋友一起看看吧
    2022-03-03
  • Go基礎(chǔ)系列:Go切片(分片)slice詳解

    Go基礎(chǔ)系列:Go切片(分片)slice詳解

    這篇文章主要介紹了Go語言中的切片(分片)slice詳細(xì)說明?,需要的朋友可以參考下
    2022-04-04
  • 基于Go語言搭建靜態(tài)文件服務(wù)器的詳細(xì)教程

    基于Go語言搭建靜態(tài)文件服務(wù)器的詳細(xì)教程

    Go 是一個(gè)開源的編程語言,它能讓構(gòu)造簡單、可靠且高效的軟件變得容易,本文給大家介紹了基于Go語言搭建靜態(tài)文件服務(wù)器的詳細(xì)教程,文中通過圖文和代碼講解的非常詳細(xì),需要的朋友可以參考下
    2024-10-10
  • 詳解Golang中g(shù)cache模塊的基本使用

    詳解Golang中g(shù)cache模塊的基本使用

    這篇文章主要通過結(jié)合商業(yè)項(xiàng)目的使用場景,為大家介紹了gcache的基本使用、緩存控制以及淘汰策略。使用gcache做緩存處理,簡單方便易上手
    2022-11-11

最新評論

青浦区| 成都市| 南木林县| 墨江| 龙门县| 霍林郭勒市| 开封县| 孝义市| 肥东县| 彝良县| 常熟市| 阳西县| 鄯善县| 尚义县| 桃园县| 景谷| 辉县市| 黎川县| 搜索| 三穗县| 仙居县| 平泉县| 绥宁县| 大安市| 湄潭县| 闸北区| 庆城县| 梨树县| 海丰县| 呼玛县| 无极县| 铜川市| 成安县| 夏津县| 黄大仙区| 观塘区| 华池县| 德州市| 贵德县| 子长县| 衡水市|