go channel关闭通道时机不对也会导致程序死锁

1 问题

今天在学习go的waitgroup时,使用10个go协程往channel中写入一个数字,然后在通过主协程把通道中的数据全部打印出来,但是奇怪的时候发生了,再关闭通道的时候,因为关闭的时机不对,导致程序发生了死锁。

2 发送死锁的代码

func wgWithDeadLock() {
	var wg sync.WaitGroup
	var pageResultChan = make(chan int, 10)

	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			pageResultChan <- i
		}(i)
	}
	wg.Wait()

	for e := range pageResultChan {
		fmt.Println(e)
	}
	close(pageResultChan)

	fmt.Println("cost time:", time.Since(start))
}

运行结果如下:

=== RUN TestWgWithDeadLock
0
1
2
3
4
5
6
7
9
8
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
testing.(*T).Run(0xc000132000, {0x9c547a?, 0x8f7c53?}, 0x9ce858)
C:/Program Files/Go/src/testing/testing.go:1487 +0x37a

testing.runTests.func1(0xc000078750?)
C:/Program Files/Go/src/testing/testing.go:1839 +0x6e
testing.tRunner(0xc000132000, 0xc000107cd8)
C:/Program Files/Go/src/testing/testing.go:1439 +0x102

testing.runTests(0xc000064320?, {0xaa9380, 0x4, 0x4}, {0x242cbd40108?, 0x40?, 0x0?})
C:/Program Files/Go/src/testing/testing.go:1837 +0x457
testing.(*M).Run(0xc000064320)
C:/Program Files/Go/src/testing/testing.go:1719 +0x5d9

main.main()
_testmain.go:53 +0x1aa

goroutine 6 [chan receive]:
learnGo/learn_sync.wgWithDeadLock()
E:/code/go/learnGo/learn_sync/multi_goroutine_fetch_data_by_page.go:113 +0x1a5
learnGo/learn_sync.TestWgWithDeadLock(0x0?)
E:/code/go/learnGo/learn_sync/multi_goroutine_fetch_data_by_page_test.go:18 +0x17
testing.tRunner(0xc0001321a0, 0x9ce858)
C:/Program Files/Go/src/testing/testing.go:1439 +0x102

created by testing.(*T).Run
C:/Program Files/Go/src/testing/testing.go:1486 +0x35f

看程序输出,结果是打印出来了,但是最后却发生了死锁,经过一番查资料,终于找到了原因:
当通道 pageResultChan 中没有数据时,for range 循环会一直阻塞在接收数据的语句上,无法继续执行下面的代码。

所以只需要把close(pageResultChan)这行代码放到for循环之前就可以了。

3 不会发生死锁的代码


func wgWithoutDeadLock() {
	var wg sync.WaitGroup
	var pageResultChan = make(chan int, 10)

	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			pageResultChan <- i
		}(i)
	}
	wg.Wait()
	close(pageResultChan) //key code

	for e := range pageResultChan {
		fmt.Println(e)
	}

	fmt.Println("cost time:", time.Since(start))
}

以下是运行结果:

=== RUN TestWgWithoutDeadLock
0
2
1
3
4
5
6
7
8
9
cost time: 508µs
— PASS: TestWgWithoutDeadLock (0.00s)
PASS

在使用channel在协程之间传递数据,特别要注意,别到底程序死锁。

你可能感兴趣的:(go,golang,javascript,算法)