4

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

Shudipta Sharma
  • 5,178
  • 3
  • 19
  • 33
sinusGob
  • 4,053
  • 12
  • 46
  • 82

2 Answers2

8

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()
}
nightfury1204
  • 4,364
  • 19
  • 22
1

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)
   }
Nordiii
  • 446
  • 1
  • 3
  • 10
  • I receive ```fatal error: all goroutines are asleep - deadlock!``` – sinusGob Nov 22 '18 at 15:07
  • The reason probably lays in your channels. Now it correctly blocks at wg.wait and your main go routine waits for ever instead of exiting early. I will update the answer in case I find something. – Nordiii Nov 22 '18 at 15:12
  • 1
    @sinusGob so you receive a deadlock because you call 'createComputer' 20 times. In 'createComputer' you ask for a Developer (20 times because you called it 20x) but you only created 5. As ' d := <-developerCreatedc' is a blocking action you end up in a deadlock because this line waits for input – Nordiii Nov 22 '18 at 15:22