紧急:同步:WaitGroup计数器为负

时间:2018-11-22 14:52:21

标签: go concurrency

我的目标是使用goroutine和通道,我想学习如何在不同goroutine之间进行通信,并且希望避免死锁。我设法使用了sync.WaitGroup,它工作正常。

但是我收到一条错误消息

  

1次紧急:同步:WaitGroup计数器为负

     

goroutine 19 [运行中]:

该程序的目标很简单。

  1. 创建开发者
  2. 指派他/她创建一个网站
  3. 取决于网站数量
  4. 完成网站后,将其添加到数组中
  5. 让我们有20个网站和5个开发人员
  6. 每个开发人员将创建4个网站并将其附加到网站数组中
  7. 我想同时执行,这样其他开发人员就不必等待

代码:

package main

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

type developer struct {
    name              string
    setTimeForWebsite time.Time
}

type website struct {
   owner   string
   created time.Time
}

var developers []developer
var websites []website

// A function to create a developer
 func hireDeveloper(wg *sync.WaitGroup, workNumber int, 
   developerCreatedc chan developer, devs []developer) {
   defer wg.Done()
   developerNumber := fmt.Sprintf("developer_%d", workNumber)
   d := developer{name: developerNumber}
   fmt.Println("Hired", d.name)
   developerCreatedc <- d
 }

 // A function to create a website
  func createComputer(wg *sync.WaitGroup, developerCreatedc chan developer, websitePerComputer int, websites []website) {
  defer wg.Done()
   // Assign the developer to the website creation // N number of the website
  d := <-developerCreatedc
  for i := 0; i <= websitePerComputer; i++ {
    fmt.Println("Delegate", d.name, "to set the time to start 
    building the website")
    d.setTimeForWebsite = time.Now()
    fmt.Println(d.name, "Finish calculating to build the website", d.setTimeForWebsite)
    web := website{owner: d.name, created: d.setTimeForWebsite}
    websites = append(websites, web)
    fmt.Println(len(websites))
    time.Sleep(time.Second * 2)
    fmt.Println(d.name, "Created website at", web.created)
   }

  }

func main() {

  // Make a channel for when developer is hired 
  developerCreatedC := make(chan developer)
  // create a sync group
  wg := &sync.WaitGroup{}
  // Assume that number of websites are 20
  numberOfWebsites := 20
  // Assume that number of developers are 5
  numberOfDevelopers := 5
  // Divide the websites to 5 developers
  websitePerDeveloper := numberOfWebsites / numberOfDevelopers
  // add the sync
  wg.Add(1)
  for i := 1; i <= numberOfDevelopers; i++ {
    go func(producerNumber int) {
        hireDeveloper(wg, producerNumber, developerCreatedC, 
        developers)
    }(i)
   }

  wg.Add(1)
  for i := 1; i <= websitePerDeveloper; i++ {
    createComputer(wg, developerCreatedC, 5, websites)
  }

  wg.Wait()
}

游乐场 https://play.golang.org/p/QSOv5jp3T94

有时这种行为有点,一个开发人员创建了4个以上的网站,即使应该只创建4个。

谢谢

2 个答案:

答案 0 :(得分:2)

wg.Add()应该等于您正在运行的go例程的数量。此外,createComputer(wg, developerCreatedC, websitePerDeveloper, websites)应该基于开发人员。

package main

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

type developer struct {
    name              string
    setTimeForWebsite time.Time
}

type website struct {
    owner   string
    created time.Time
}

var developers []developer
var websites []website

// A function to create a developer
func hireDeveloper(wg *sync.WaitGroup, workNumber int, developerCreatedc chan developer, devs []developer) {
    defer wg.Done()
    developerNumber := fmt.Sprintf("developer_%d", workNumber)
    d := developer{name: developerNumber}
    fmt.Println("Hired", d.name)
    developerCreatedc <- d
}

// A function to create a website
func createComputer(wg *sync.WaitGroup, developerCreatedc chan developer, websitePerComputer int, websites []website) {
    defer wg.Done()
    // Assign the developer to the website creation // N number of the website
    d := <-developerCreatedc
    for i := 0; i <= websitePerComputer; i++ {
        fmt.Println("Delegate", d.name, "to set the time to start building the website")
        d.setTimeForWebsite = time.Now()
        web := website{owner: d.name, created: d.setTimeForWebsite}
        websites = append(websites, web)
        fmt.Println(len(websites))
        time.Sleep(time.Second * 2)
        fmt.Println(d.name, "Created website at", web.created)
    }

}

func main() {

    // Make a channel for when developer is hired 
    developerCreatedC := make(chan developer)
    // create a sync group
    wg := &sync.WaitGroup{}
    // Assume that number of websites are 20
    numberOfWebsites := 20
    // Assume that number of developers are 5
    numberOfDevelopers := 5
    // Divide the websites to 5 developers
    websitePerDeveloper := numberOfWebsites / numberOfDevelopers

    for i := 1; i <= numberOfDevelopers; i++ {
        // add the sync
        wg.Add(1)
        go func(producerNumber int) {
            hireDeveloper(wg, producerNumber, developerCreatedC, developers)
        }(i)

        wg.Add(1)
        go createComputer(wg, developerCreatedC, websitePerDeveloper, websites)
    }

    wg.Wait()
}

答案 1 :(得分:0)

收到错误是因为您在循环之前执行了wg.Add(1)。 每次对hireDeveloper()createComputer()的调用都会调用wg.Done(),因此已经在第一个for循环wg中倒数到-4,这是不可能的,因此很恐慌。

可能的解决方案是:

wg.Add(numberOfDevelopers)
for i := 1; i <= numberOfDevelopers; i++ {...}
....
wg.Add(websitePerDeveloper)
for i := 1; i <= websitePerDeveloper; i++ {...}

或者您将wg.Add插入for循环:

      for i := 1; i <= numberOfDevelopers; i++ {
        wg.Add(1)
        go func(producerNumber int) {
            hireDeveloper(wg, producerNumber, developerCreatedC, 
            developers)
        }(i)
   }