go 发生死锁的问题

2020-06-02 15:45:32 +08:00
 liu826250634
package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

var wg sync.WaitGroup

//计算一个 64 位随机数的各位的和
func randNumber(x int64) int64 {
	var sum int64 = 0
	for x > 0 {
		a := x % 10
		x = x / 10
		sum += a
	}
	return sum
}

func main() {
	wg.Add(25)
	var jobChan = make(chan int64, 10)
	var resultChan = make(chan int64, 10)
	go func(jobChan chan<- int64, ) {
		defer wg.Done()
		for i:=0;i<1000;i++{
			rand.Seed(time.Now().UnixNano())
			jobChan <- rand.Int63n(100)
		}
	}(jobChan)

	for i:=0;i<24;i++{
		go func(jobChan <-chan int64, resultChan chan<- int64, ) {
			defer wg.Done()
			for num:= range jobChan{
				resultChan <- randNumber(num)
			}
		}(jobChan, resultChan)
	}

	for i:= range resultChan{
		fmt.Println(i)
	}
	wg.Wait()
}


发生错误:fatal error: all goroutines are asleep - deadlock!
请教一下,为什么会发生死锁的情况?
2860 次点击
所在节点    Go 编程语言
7 条回复
linjunyi22
2020-06-02 17:20:09 +08:00
```go
package main

import (
"fmt"
"math/rand"
"sync"
"time"
)

var wg sync.WaitGroup

//计算一个 64 位随机数的各位的和
func randNumber(x int64) int64 {
var sum int64 = 0
for x > 0 {
a := x % 10
x = x / 10
sum += a
}
return sum
}

func main() {
wg.Add(25)
var jobChan = make(chan int64, 10)
var resultChan = make(chan int64, 10)
go func(jobChan chan<- int64, ) {
defer wg.Done()
for i := 0; i < 1000; i++ {
rand.Seed(time.Now().UnixNano())
jobChan <- rand.Int63n(100)
}
close(jobChan) // 此处要关闭 jobChan,否则在 jobChan 的 range 遍历中会一直阻塞
}(jobChan)

for i := 0; i < 24; i++ {
go func(jobChan <-chan int64, resultChan chan<- int64, ) {
defer wg.Done()
for num := range jobChan {
resultChan <- randNumber(num)
}
}(jobChan, resultChan)
}
// 此处 resultChan 的遍历放到一个 goroutine 中执行,让 wg 的 wait 执行完后主协程直接退出
// 如果放在主协程中,也没有关闭 resultChan 的话,也会造成阻塞,就会产生死锁
go func() {
for i := range resultChan {
fmt.Println(i)
}
}()
wg.Wait()
}
```
beidounanxizi
2020-06-02 17:21:04 +08:00
去看看 waitgroup 实现吧
evill
2020-06-02 17:36:57 +08:00
channel 没有关闭
for num:= range jobChan
永远不会退出
zjj19950716
2020-06-02 17:39:22 +08:00
for num:= range jobChan{
resultChan <- randNumber(num)
}
job 1000 个发完之后,这边还一直卡主。
最后的 Wait()也没意义,从 resultChan 读那里也退不出来
asAnotherJack
2020-06-02 17:41:24 +08:00
原因就是 2l 说的那样,因为 jobChan 和 resultChan 没有 close 阻塞在循环里了,jobChan 就像 2l 那样处理,resultChan 的处理,可以在 wg.Wait()后边 close resultChan,然后再 range resultChan 打印结果
liu826250634
2020-06-03 17:39:51 +08:00
@linjunyi22 感谢, 刚学习 go,有一些概念改搞不清。但是你这种方法好像会造成接收不完全就会退出。我自己也解决了这个问题了。

```
package main

import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)

var (
wg = sync.WaitGroup{} // 用于计数, 让程序正常执行,不会主函数执行完子函数还没执行。计数清 0 则不用等待
a int
//lock sync.Mutex
rwlock sync.RWMutex
icons map[string]string
loadIconsOnce sync.Once
//m sync.Map
)

func randNumber(x int64) int64 {
var sum int64 = 0
for x > 0 {
a := x % 10
x = x / 10
sum += a
}
return sum
}


func main() {
wg.Add(25)
defer wg.Wait()
var maxSend = 10
var jobChan = make(chan int64, 10)
var resultChan = make(chan int64, 10)
var lock sync.Mutex
ctx, cancel := context.WithCancel(context.Background())

//var once sync.Once
go func(jobChan chan<- int64, ) {
for i:=0;i<maxSend;i++{
rand.Seed(time.Now().UnixNano())
jobChan <- rand.Int63n(100)
}
close(jobChan)
wg.Done()
}(jobChan)

count1 := 1
for i:=0;i<24;i++{
go func(jobChan <-chan int64, resultChan chan int64, ctx context.Context) {
defer wg.Done()
for num:= range jobChan{
select {
case <- ctx.Done():
return
case resultChan <- randNumber(num):
lock.Lock()
fmt.Println("count:", count1)
count1 += 1
lock.Unlock()
}

}
}(jobChan, resultChan, ctx)
}

num := maxSend
count := 1
for value := range resultChan {
if num == 1 {
fmt.Printf("key:%v, value:%v\n", count, value)
cancel()
return
}else {
num -= 1
fmt.Printf("key:%v, value:%v\n", count, value)
count += 1
}
}
}

```
liu826250634
2020-06-03 17:58:10 +08:00
回复中, 写代码怎么 md 不生效了= =

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/677881

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX