通过缓冲通道(Golang)限制并发执行进程的数量

通过缓冲通道(Golang)限制并发执行进程的数量,go,concurrency,channel,Go,Concurrency,Channel,意图: 我正在寻找一种并行运行os级shell命令的方法,但要小心不要影响CPU,我想知道缓冲通道是否适合这个用例 实施: 创建一系列具有模拟运行时持续时间的作业s。将这些作业发送到一个队列,该队列将将它们分派到运行的缓冲通道上,该通道由EXEC\u THROTTLE限制 意见: 这“起作用”(就编译和运行而言),但我想知道缓冲区是否按照指定的方式工作(请参阅“Intent”),以限制并行运行的进程数量 免责声明: 现在,我意识到新手往往会过度使用频道,但我觉得这种对洞察力的要求是诚实的,因为我

意图:

我正在寻找一种并行运行os级shell命令的方法,但要小心不要影响CPU,我想知道缓冲通道是否适合这个用例

实施:

创建一系列具有模拟运行时持续时间的
作业
s。将这些作业发送到一个队列,该队列将
它们分派到
运行
的缓冲通道上,该通道由
EXEC\u THROTTLE
限制

意见:

这“起作用”(就编译和运行而言),但我想知道缓冲区是否按照指定的方式工作(请参阅“Intent”),以限制并行运行的进程数量

免责声明:

现在,我意识到新手往往会过度使用频道,但我觉得这种对洞察力的要求是诚实的,因为我至少克制了自己使用了
sync.WaitGroup
。请原谅这个有点像玩具的例子,但所有的洞察力都将受到赞赏

主程序包
进口(
//“os/exec”
“日志”
“数学/兰德”
“strconv”
“同步”
“时间”
)
常数(
执行节流阀=2
)
类型JobsManifest[]作业
类型作业结构{
cmd字符串
结果字符串
运行时int//模拟长时间运行的任务
}

func(j JobsManifest)queueJobs(logChan chan如果我没弄错的话,你的意思是建立一种机制,以确保在任何时候最多有多个
EXEC\u THROTTLE
作业在运行。如果这是你的意图,那么代码就不起作用了

这是因为当你启动一个作业时,你已经消耗了通道-允许启动另一个作业,但没有完成任何作业。你可以通过添加一个计数器(你需要原子添加或互斥)来调试它

执行作业时,只需启动一组具有无缓冲通道和块的goroutine即可完成此工作:

func Run(j Job) r Result {
    //Run your job here
}

func Dispatch(ch chan Job) {
    for j:=range ch {
        wg.Add(1)
        Run(j)
        wg.Done()
    }
}

func main() {
    ch := make(chan Job)
    for i:=0; i<EXEC_THROTTLE; i++ {
        go Dispatch(ch)
    }
    //call dispatch according to the queue here.
}
func运行(j作业)r结果{
//在这里管理你的工作
}
func调度(总陈工){
对于j:=范围ch{
工作组.添加(1)
运行(j)
wg.Done()
}
}
func main(){
ch:=制造(成龙工作)

对于i:=0;i如果我理解正确,您的意思是建立一种机制,以确保在任何时候最多有多个
EXEC\u THROTTLE
作业正在运行。如果这是您的意图,则代码不起作用

这是因为当你启动一个作业时,你已经消耗了通道-允许启动另一个作业,但没有完成任何作业。你可以通过添加一个计数器(你需要原子添加或互斥)来调试它

执行作业时,只需启动一组具有无缓冲通道和块的goroutine即可完成此工作:

func Run(j Job) r Result {
    //Run your job here
}

func Dispatch(ch chan Job) {
    for j:=range ch {
        wg.Add(1)
        Run(j)
        wg.Done()
    }
}

func main() {
    ch := make(chan Job)
    for i:=0; i<EXEC_THROTTLE; i++ {
        go Dispatch(ch)
    }
    //call dispatch according to the queue here.
}
func运行(j作业)r结果{
//在这里管理你的工作
}
func调度(总陈工){
对于j:=范围ch{
工作组.添加(1)
运行(j)
wg.Done()
}
}
func main(){
ch:=制造(成龙工作)
对于i:=0;i我经常使用它

我经常用这个


您还可以使用缓冲通道限制并发性:

concurrencyLimit := 2 // Number of simultaneous jobs.
limiter := make(chan struct{}, concurrencyLimit)
for job := range jobs {
    job := job // Pin loop variable.
    limiter <- struct{}{} // Reserve limiter slot.
    go func() {
        defer func() {
            <-limiter // Free limiter slot.
        }()

        do(job) // Do the job.
    }()
}
// Wait for goroutines to finish by filling full channel.
for i := 0; i < cap(limiter); i++ {
    limiter <- struct{}{}
}
concurrentylimit:=2//同时作业的数量。
限制器:=make(chan结构{},并发限制)
对于作业:=范围作业{
作业:=作业//针循环变量。

限制器您还可以使用缓冲通道限制并发:

concurrencyLimit := 2 // Number of simultaneous jobs.
limiter := make(chan struct{}, concurrencyLimit)
for job := range jobs {
    job := job // Pin loop variable.
    limiter <- struct{}{} // Reserve limiter slot.
    go func() {
        defer func() {
            <-limiter // Free limiter slot.
        }()

        do(job) // Do the job.
    }()
}
// Wait for goroutines to finish by filling full channel.
for i := 0; i < cap(limiter); i++ {
    limiter <- struct{}{}
}
concurrentylimit:=2//同时作业的数量。
限制器:=make(chan结构{},并发限制)
对于作业:=范围作业{
作业:=作业//针循环变量。

限制器将processItem函数替换为所需的作业执行

下面将按正确顺序执行作业。Atmost EXEC_并发项将同时执行

package main

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

func processItem(i int, done chan int, wg *sync.WaitGroup) { 
    fmt.Printf("Async Start: %d\n", i)
    time.Sleep(100 * time.Millisecond * time.Duration(i))
    fmt.Printf("Async Complete: %d\n", i)
    done <- 1
    wg.Done()
}

func popItemFromBufferChannelWhenItemDoneExecuting(items chan int, done chan int) { 
    _ = <- done
    _ = <-items
}


func main() {
    EXEC_CONCURRENT := 3

    items := make(chan int, EXEC_CONCURRENT)
    done := make(chan int)
    var wg sync.WaitGroup

    for i:= 1; i < 11; i++ {
        items <- i
        wg.Add(1)   
        go processItem(i, done, &wg)
        go popItemFromBufferChannelWhenItemDoneExecuting(items, done)
    }

    wg.Wait()
}
package main

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

func processItem(i int, items chan int, wg *sync.WaitGroup) { 
    items <- i
    fmt.Printf("Async Start: %d\n", i)
    time.Sleep(100 * time.Millisecond * time.Duration(i))
    fmt.Printf("Async Complete: %d\n", i)
    _ = <- items
    wg.Done()
}

func main() {
    EXEC_CONCURRENT := 3

    items := make(chan int, EXEC_CONCURRENT)
    var wg sync.WaitGroup

    for i:= 1; i < 11; i++ {
        wg.Add(1)   
        go processItem(i, items, &wg)
    }

    wg.Wait()
}
主程序包
进口(
“fmt”
“同步”
“时间”
)
func processItem(i int,done chan int,wg*sync.WaitGroup){
fmt.Printf(“异步启动:%d\n”,i)
时间。睡眠(100*时间。毫秒*时间。持续时间(i))
fmt.Printf(“异步完成:%d\n”,i)

完成用所需的作业执行替换processItem函数

下面将按正确顺序执行作业。Atmost EXEC_并发项将同时执行

package main

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

func processItem(i int, done chan int, wg *sync.WaitGroup) { 
    fmt.Printf("Async Start: %d\n", i)
    time.Sleep(100 * time.Millisecond * time.Duration(i))
    fmt.Printf("Async Complete: %d\n", i)
    done <- 1
    wg.Done()
}

func popItemFromBufferChannelWhenItemDoneExecuting(items chan int, done chan int) { 
    _ = <- done
    _ = <-items
}


func main() {
    EXEC_CONCURRENT := 3

    items := make(chan int, EXEC_CONCURRENT)
    done := make(chan int)
    var wg sync.WaitGroup

    for i:= 1; i < 11; i++ {
        items <- i
        wg.Add(1)   
        go processItem(i, done, &wg)
        go popItemFromBufferChannelWhenItemDoneExecuting(items, done)
    }

    wg.Wait()
}
package main

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

func processItem(i int, items chan int, wg *sync.WaitGroup) { 
    items <- i
    fmt.Printf("Async Start: %d\n", i)
    time.Sleep(100 * time.Millisecond * time.Duration(i))
    fmt.Printf("Async Complete: %d\n", i)
    _ = <- items
    wg.Done()
}

func main() {
    EXEC_CONCURRENT := 3

    items := make(chan int, EXEC_CONCURRENT)
    var wg sync.WaitGroup

    for i:= 1; i < 11; i++ {
        wg.Add(1)   
        go processItem(i, items, &wg)
    }

    wg.Wait()
}
主程序包
进口(
“fmt”
“同步”
“时间”
)
func processItem(i int,done chan int,wg*sync.WaitGroup){
fmt.Printf(“异步启动:%d\n”,i)
时间。睡眠(100*时间。毫秒*时间。持续时间(i))
fmt.Printf(“异步完成:%d\n”,i)

谢谢你的反馈@leadbebop,我会尝试建议并接受答案,一旦我整理好了。你可能会发现这篇博文很有帮助。嗨@leadbebop。也许我对你的评论感到困惑。
//根据这里的队列进行呼叫调度。
。我更新了队列以循环,直到
EXEC\u THROTTLE
的长度,但正如你所说如果
EXEC\u THROTTLE==2
,则只能看到前两个
作业运行。我缺少什么?另外,我想我需要等待
logger
返回调用
wg.Done()
。你确实没有领会我的意思。我在这里更改了你的代码:首先通过我在答案中的解释来理解它。我发现很难解释更多,但如果还有什么不清楚的地方,请尽管问。谢谢。我以前觉得有必要在goroutine中调用
run
,原因是我想阻止EXEC\u THROTTLE
,而不是每个
作业
。经过编辑,似乎每个
作业
都是按顺序运行的,而不是并行执行前两个,但现在通过研究您的答案,我意识到这是我的误解,因为它在
运行
返回后立即发送到
记录器
Job
s的air具有与
runtime
相同的值,很明显它是并行执行的。我真的很感谢你花时间来澄清这一点。感谢你的反馈@leadbebebop,我会尝试建议并接受答案,一旦我整理好了。你可能会发现这篇博客文章很有帮助。嗨@leadbebebop。也许我是conf用于你的通讯