Issue with goroutine and channel


(Jameswang2015) #1

program get stuck with running this code.

package main

import (
	_ "bufio"
	"fmt"
	_ "net/http"
	"time"
)

const (
	numberOfWorkers int = 3
	numberOfTasks int = 5
)

func worker(id int, url chan string, result chan bool){
	for receivedURL := range url {
		fmt.Printf("worker %d start job %s\n", id, receivedURL)
		time.Sleep(1 * time.Second)
		fmt.Printf("worker %d finish job %s\n", id, receivedURL)
		result <- true
	}
}

func main() {
	url := make(chan string)
	result := make(chan bool)
	urls := []string{
		"http://gobyexample.com",
		"http://att.com",
		"http://domaintools.com",
		"http://microsoft.com",
		"http://google.com",
	}
	for i := 0; i < numberOfWorkers; i++ {
		go worker(i, url, result)
	}
	for j := 0; j < numberOfTasks; j++ {
		url <- urls[j]
	}
	close(url)

	for t := 0; t < numberOfTasks; t++ {
		//<-result
		if <-result {
			fmt.Printf("get %d done\n", t)
		}
	}

}

here is the output:

(base) zwang-mac:cmd1 zwang$ go run httpRoutine5-1.go
worker 2 start job http://gobyexample.com
worker 1 start job http://domaintools.com
worker 0 start job http://att.com
worker 2 finish job http://gobyexample.com
worker 0 finish job http://att.com
worker 1 finish job http://domaintools.com

it stucks there and kind of dead lock though I didn’t get that error message.

when I change the two unbuffered channels to buffered channels as bellow, it does run through successfully. I’m wondering what’s wrong with the first version?

package main

import (
	_ "bufio"
	"fmt"
	_ "net/http"
	"time"
)

const (
	numberOfWorkers int = 3
	numberOfTasks int = 5
)

func worker(id int, url chan string, result chan bool){
	for receivedURL := range url {
		fmt.Printf("worker %d start job %s\n", id, receivedURL)
		time.Sleep(1 * time.Second)
		fmt.Printf("worker %d finish job %s\n", id, receivedURL)
		result <- true
	}
}

func main() {
	url := make(chan string, 100)
	result := make(chan bool, 100)
	urls := []string{
		"http://gobyexample.com",
		"http://att.com",
		"http://domaintools.com",
		"http://microsoft.com",
		"http://google.com",
	}
	for i := 0; i < numberOfWorkers; i++ {
		go worker(i, url, result)
	}
	for j := 0; j < numberOfTasks; j++ {
		url <- urls[j]
	}
	close(url)

	for t := 0; t < numberOfTasks; t++ {
		//<-result
		if <-result {
			fmt.Printf("get %d done\n", t)
		}
	}

}

(Norbert Melzer) #2

Because of all channels beeing unbuffered, the main go routine will eventually try to send the next work item, while all workers are trying to send their result back, which won’t be read before all workitems have been sent.