首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >高钢职工池实现意外工作

高钢职工池实现意外工作
EN

Stack Overflow用户
提问于 2022-08-21 12:09:00
回答 1查看 246关注 0票数 2

我已经实现了一个黄金工人池如下,其中sem和工作是渠道。sem是跟踪当前活跃的工作人员(Goroutines)数量的一个渠道。work是将函数传递给活动员工执行的通道。超时将返回超时期间空闲的任何员工。

代码语言:javascript
复制
package main

import (
    "time"
)

type Pool struct {
    sem chan struct{}
    work chan func()
    timeout time.Duration
}

func NewPool(max, size, spawn int, timeout time.Duration) *Pool {
    if spawn <= 0 {
        panic("workpool spawn is <= 0")
    }
    if spawn > max {
        panic("workpool spawn > max workers")
    }
    p := &Pool{
        sem: make(chan struct{}, max),
        work: make(chan func(), size),
        timeout: timeout,
    }
    for i := 0; i < spawn; i++ {
        p.sem <- struct{}{}
        go p.worker(func() {})
    }
    return p

}

func (p *Pool) AddTask(task func()) {
    select {
        case p.work <- task:
            return
        case p.sem <- struct{}{}:
            go p.worker(task)
            return
    }
}

func (p *Pool) worker(task func()) {
    t := time.NewTimer(p.timeout)
    defer func() {
        t.Stop()
        <- p.sem
    }()

    task()

    for {
        select {
            case task := <- p.work:
                t.Reset(p.timeout)
                task()
            case <- t.C:
                return
        }
    }
}

我是通过在for循环中打印i的值来测试的,方法是将它传递到以匿名函数包装的池中,如下所示:

代码语言:javascript
复制
package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("Hello, world!")

    p := NewPool(3, 10, 1, time.Duration(5) * time.Second)

    for i:=0; i<30; i++ {
        p.AddTask(func () {
            fmt.Print(i, " ")
        })
    }
    time.Sleep(10 * time.Second)
    fmt.Println("End")

}

预期的输出应该是0到29之间的序列号,但是输出应该是

代码语言:javascript
复制
Hello, world!
12 12 12 12 12 12 12 12 12 12 12 12 13 25 25 25 25 25 25 25 25 25 25 25 26 25 30 30 30 30 End

我不明白为什么输出是这样的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-21 12:35:38

您的函数闭包都引用了相同的i值。这会创建一个争用条件,当函数被分派时,它们正在读取一个不断变化的值--因此您将看到不可预测的输出。

要确保闭包获得唯一值,请在循环中声明变量。要做到这一点,一个简单的技巧是通过阴影声明相同的变量名i := i

代码语言:javascript
复制
for i:=0; i<30; i++ {
    i:= i                // <- add this
    p.AddTask(func () {
        fmt.Print(i, " ")
    })
}

https://go.dev/play/p/o0Nyx5A46tp

顺便说一句,这种技术在有效围棋文档中已经讨论过了,请参阅本节:

写起来似乎很奇怪 req := req 但这么做是合法的,也是惯用的。您将得到同名变量的新版本,故意在本地跟踪循环变量,但对每个goroutine都是唯一的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73434264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档