Go 通过通道发送的值不视为已接收

Go 通过通道发送的值不视为已接收,go,channel,goroutine,Go,Channel,Goroutine,下面的代码启动了一些工作人员。每个worker通过一个通道接收一个值,该通道添加到一个映射中,其中键是worker ID,值是接收到的数字。最后,当我添加所有接收到的值时,我应该得到一个预期结果(在本例中为55,因为这是从1..10添加时得到的结果)。在大多数情况下,我没有看到预期的产出。我做错了什么?我不想通过增加睡眠来解决这个问题。我希望以编程方式确定问题并修复它 type counter struct { value int count int } var data map[st

下面的代码启动了一些工作人员。每个worker通过一个通道接收一个值,该通道添加到一个映射中,其中键是worker ID,值是接收到的数字。最后,当我添加所有接收到的值时,我应该得到一个预期结果(在本例中为55,因为这是从1..10添加时得到的结果)。在大多数情况下,我没有看到预期的产出。我做错了什么?我不想通过增加睡眠来解决这个问题。我希望以编程方式确定问题并修复它

type counter struct {
  value int
  count int
}

var data map[string]counter
var lock sync.Mutex

func adder(wid string, n int) {
  defer lock.Unlock()
  lock.Lock()
  d := data[wid]
  d.count++
  d.value += n
  data[wid] = d

  return
}

func main() {
  fmt.Println(os.Getpid())
  data = make(map[string]counter)
  c := make(chan int)
  for w := 1; w <= 3; w++ {  //starting 3 workers here
    go func(wid string) {
      data[wid] = counter{}
      for {
        v, k := <-c
        if !k {
          continue
        }
        adder(wid, v)
      }
    }(strconv.Itoa(w))  // worker is given an ID 
  }
  time.Sleep(1 * time.Second) // If this is not added, only one goroutine is recorded.
  for i := 1; i <= 10; i++ {
    c <- i
  }
  close(c)
  total := 0
  for i, v := range data {
    fmt.Println(i, v)
    total += v.value
  }
  fmt.Println(total)
}
类型计数器结构{
值int
计数整数
}
var数据映射[字符串]计数器
var lock sync.Mutex
func加法器(wid字符串,n int){
延迟锁定。解锁()
lock.lock()
d:=数据[wid]
d、 计数++
d、 值+=n
数据[wid]=d
回来
}
func main(){
fmt.Println(os.Getpid())
数据=生成(映射[字符串]计数器)
c:=制造(成交量)

对于w:=1;w您的代码有两个重要的种族:

  • data[wid]=计数器{}
    的初始化与可能正在读取和重写
    数据的其他goroutine不同步
  • worker goroutine在完成修改
    数据
    时不会发出信号,这意味着您的主goroutine可能会在完成写入之前读取
    数据
您还有一个奇怪的构造:

for {
    v, k := <-c
    if !k {
        continue
    }
    adder(wid, v)
}

(这很容易改进,例如,
wg
没有理由全球化。)

好吧,我喜欢@torek的答案,但我想发布这个答案,因为它包含了一系列改进:

  • 减少锁的使用(对于这样简单的任务,避免使用锁。如果您对其进行基准测试,您会注意到一个很好的区别,因为我的代码只使用锁
    numworkers
    次)
  • 改进变量的命名
  • 删除全局变量的使用(全局变量的使用应始终尽可能少)
  • 下面的代码使用
    numWorker
    生成的goroutines将一个数字从
    minWork
    添加到
    maxWork

    主程序包
    进口(
    “fmt”
    “同步”
    )
    常数(
    bufferSize=1//numChan的缓冲区
    numworkers=3//进行加法的工人数
    minWork=1//来自[minWork]的总和(含)
    maxWork=10000000//总计达到[maxWork](含)
    )
    //工人统计
    类型工作结构{
    workCount int//工人工作的次数
    workDone int//完成的工作量;添加的数字
    }
    //workerMap保存一个或多个worker的映射
    类型workerMap结构{
    mu sync.Mutex//保护m以实现安全的并发r/w
    m map[int]worker//map用于保存worker id到worker映射
    }
    func main(){
    变量(
    TotalWorkOne int//完成的总工时
    wm workerMap//workerMap
    wg sync.WaitGroup//WaitGroup
    numChan=make(chan int,bufferSize)//用于NUM的通道
    )
    wm.m=make(映射[int]工作者,numworkers)
    对于wid:=0;widpackage main
    
    import (
        "fmt"
        // "os"
        "strconv"
        "sync"
        // "time"
    )
    
    type counter struct {
        value int
        count int
    }
    
    var data map[string]counter
    var lock sync.Mutex
    var wg sync.WaitGroup
    
    func adder(wid string, n int) {
        defer lock.Unlock()
        lock.Lock()
        d := data[wid]
        d.count++
        d.value += n
        data[wid] = d
    }
    
    func main() {
        // fmt.Println(os.Getpid())
        data = make(map[string]counter)
        c := make(chan int)
        for w := 1; w <= 3; w++ { //starting 3 workers here
            wg.Add(1)
            go func(wid string) {
                lock.Lock()
                data[wid] = counter{}
                lock.Unlock()
                for v := range c {
                    adder(wid, v)
                }
                wg.Done()
            }(strconv.Itoa(w)) // worker is given an ID
        }
        for i := 1; i <= 10; i++ {
            c <- i
        }
        close(c)
        wg.Wait()
        total := 0
        for i, v := range data {
            fmt.Println(i, v)
            total += v.value
        }
        fmt.Println(total)
    }