Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

panic: sync: negative WaitGroup counter

Tags:

concurrency

go

My goal is to use goroutines and channel, i want to learn how to communicate between different goroutines and i want to avoid deadlock. I managed to use sync.WaitGroup and it is working just fine.

However I received an error saying that

1 panic: sync: negative WaitGroup counter

goroutine 19 [running]:

The goal of this program is simple.

  1. Create a developer
  2. Assign him/her to create a website
  3. Depends on the number of the websites
  4. Once website is done then append it to the array
  5. Let there are 20 websites and 5 developers
  6. Each developer will take create 4 websites and append it to the websites array
  7. I want to do it concurrently so that other developers don't have to wait

The code:

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()
}

The playground https://play.golang.org/p/QSOv5jp3T94

The behaviour is a bit sometimes, one developer created more than 4 websites, even though it is supposed to create only 4

Thanks

like image 967
sinusGob Avatar asked Jan 28 '23 03:01

sinusGob


2 Answers

wg.Add() should be equal to the number number of go routine you are running. Also createComputer(wg, developerCreatedC, websitePerDeveloper, websites) should be per developer basis.

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()
}
like image 167
nightfury1204 Avatar answered Feb 14 '23 13:02

nightfury1204


You receive the error because you do wg.Add(1) before the loop. Every call to hireDeveloper() and createComputer() calls wg.Done(), so already in the first for loop wg wants to count down till -4 which is not possible thus the panic.

A possible solution would be:

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

or you pull wg.Add into the for loop:

      for i := 1; i <= numberOfDevelopers; i++ {
        wg.Add(1)
        go func(producerNumber int) {
            hireDeveloper(wg, producerNumber, developerCreatedC, 
            developers)
        }(i)
   }
like image 23
Nordiii Avatar answered Feb 14 '23 12:02

Nordiii