如何避免并发打印字母数字时的死锁问题

huangapple go评论74阅读模式
英文:

How to avoid deadlock in printing alphanumeric numbers concurrently

问题

我是你的中文翻译助手,以下是你要翻译的内容:

我刚开始学习golang,我希望打印出10个由数字范围和字符范围组合而成的字母数字串。

我决定使用并发来实现,但是我遇到了一个关于死锁的错误。

package main

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

type alphanumeric struct {
	anAlphabet string
	aNumber    string
}

func (someStruct alphanumeric) pairAlphanumeric() string {
	return someStruct.aNumber + someStruct.anAlphabet
}

func main() {
	var wg sync.WaitGroup

	numbers := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
	alphabets := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}

	wg.Add(len(alphabets))
	go func(numbers []string, alphabets []string) {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			makeAleph(numbers, alphabets)
		}
	}(numbers, alphabets)

	wg.Wait()
}

func makeAleph(numbers []string, alphabets []string) {
	var aleph alphanumeric

	aleph.anAlphabet = aNum(numbers)
	aleph.aNumber = anAlph(alphabets)

	fmt.Println(aleph.pairAlphanumeric())
}

func randomIndex() int {
	randTime := time.Time.UnixNano(time.Now())
	rand.Seed(randTime)
	return rand.Intn(10)
}

func aNum(numbers []string) string {
	return numbers[randomIndex()]
}

func anAlph(alphabets []string) string {
	return alphabets[randomIndex()]
}

在正确打印所需的数字后,它抛出以下错误:

❯ go run aleph.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc42000e2dc)
/Users/eklavya/.gvm/gos/go1.8/src/runtime/sema.go:47 +0x34
sync.(*WaitGroup).Wait(0xc42000e2d0)
/Users/eklavya/.gvm/gos/go1.8/src/sync/waitgroup.go:131 +0x7a
main.main()
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:38 +0x14c
goroutine 5 [chan receive (nil chan)]:
main.makeAleph(0xc420084000, 0xa, 0xa, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:61 +0x134
main.main.func1(0xc42000e2d0, 0xc420084000, 0xa, 0xa, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:35 +0x94
created by main.main
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:37 +0x13e
goroutine 6 [chan send (nil chan)]:
main.aNum(0x0, 0xc420084000, 0xa, 0xa)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:79 +0x5b
main.makeAleph.func1(0xc42000e2e0, 0x0, 0xc420084000, 0xa, 0xa)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:51 +0x73
created by main.makeAleph
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:52 +0xad
goroutine 7 [chan send (nil chan)]:
main.anAlph(0x0, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:85 +0x5b
main.makeAleph.func2(0xc42000e2e0, 0x0, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:56 +0x73
created by main.makeAleph
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:57 +0xff
exit status 2

如何避免在并发打印字母数字时发生死锁?

英文:

I'm new to golang and I just wish to print out 10 alphanumeric numbers combining elements from numeber-range and character-range.

I decided to do it concurrently, but I've been running into an error regarding deadlock.

package main
import (
&quot;fmt&quot;
&quot;math/rand&quot;
&quot;sync&quot;
&quot;time&quot;
)
type alphanumeric struct {
anAlphabet string
aNumber    string
}
func (someStruct alphanumeric) pairAlphanumeric() string {
return someStruct.aNumber + someStruct.anAlphabet
}
func main() {
var wg sync.WaitGroup
numbers := []string{&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;, &quot;7&quot;, &quot;8&quot;, &quot;9&quot;, &quot;10&quot;}
alphabets := []string{&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;, &quot;g&quot;, &quot;h&quot;, &quot;i&quot;, &quot;j&quot;, &quot;k&quot;, &quot;l&quot;, &quot;m&quot;, &quot;n&quot;, &quot;o&quot;, &quot;p&quot;, &quot;q&quot;, &quot;r&quot;, &quot;s&quot;, &quot;t&quot;, &quot;u&quot;, &quot;v&quot;, &quot;w&quot;, &quot;x&quot;, &quot;y&quot;, &quot;z&quot;}
//var aleph alphanumeric
//var alephS []alphanumeric
wg.Add(len(alphabets))
go func(numbers []string, alphabets []string) {
defer wg.Done()
for i := 0; i &lt; 10; i++ {
makeAleph(numbers, alphabets)
}
}(numbers, alphabets)
wg.Wait()
} // end of main()
func makeAleph(numbers []string, alphabets []string) {
var aleph alphanumeric
aleph.anAlphabet = aNum(numbers)
aleph.aNumber = anAlph(alphabets)
fmt.Println(aleph.pairAlphanumeric())
//return aleph.pairAlphanumeric()
}
func randomIndex() int {
randTime := time.Time.UnixNano(time.Now())
rand.Seed(randTime)
return rand.Intn(10)
}
func aNum(numbers []string) string {
return numbers[randomIndex()]
}
func anAlph(alphabets []string) string {
return alphabets[randomIndex()]
}

And the error that it throws after printing the required numbers correctly is:

❯ go run aleph.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc42000e2dc)
/Users/eklavya/.gvm/gos/go1.8/src/runtime/sema.go:47 +0x34
sync.(*WaitGroup).Wait(0xc42000e2d0)
/Users/eklavya/.gvm/gos/go1.8/src/sync/waitgroup.go:131 +0x7a
main.main()
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:38 +0x14c
goroutine 5 [chan receive (nil chan)]:
main.makeAleph(0xc420084000, 0xa, 0xa, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:61 +0x134
main.main.func1(0xc42000e2d0, 0xc420084000, 0xa, 0xa, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:35 +0x94
created by main.main
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:37 +0x13e
goroutine 6 [chan send (nil chan)]:
main.aNum(0x0, 0xc420084000, 0xa, 0xa)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:79 +0x5b
main.makeAleph.func1(0xc42000e2e0, 0x0, 0xc420084000, 0xa, 0xa)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:51 +0x73
created by main.makeAleph
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:52 +0xad
goroutine 7 [chan send (nil chan)]:
main.anAlph(0x0, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:85 +0x5b
main.makeAleph.func2(0xc42000e2e0, 0x0, 0xc420001520, 0x1a, 0x1a)
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:56 +0x73
created by main.makeAleph
/Users/eklavya/Projects/Polyglot/TedTalks/goTED/experiments/async/aleph.go:57 +0xff
exit status 2

How can I avoid deadlock in printing alphanumeric numbers concurrently?

答案1

得分: 0

你当前的代码根本不是并发的。所有的字母数字代码都是在一个单独的for循环中按顺序生成的,该循环在除了main之外的唯一goroutine中运行。

你将len(alphabets) == 26添加到了wg.Wait中。这意味着你需要调用26次wg.Done才能完成对wg.Wait的调用。每次调用wg.Done都会将等待组计数器减一。

在你的代码中,你只调用了一次wg.Done。这意味着一旦你的goroutine返回,等待组计数器就会保持在25,而调用wg.Wait将永远不会返回,因为没有其他的goroutine在运行,也就无法通过进一步调用wg.Done来减少等待组计数器。

为了(模糊地)实现你想要的效果,你可以尝试像这样做:

// ...

n := 10 // 你想要打印的代码数量

wg.Add(n)

for i := 0; i < n; i++ {
go func(numbers []string, alphabets []string) {
defer wg.Done()
makeAleph(numbers, alphabets)
}(numbers, alphabets)
}

wg.Wait()

// ...

现在,将会生成n个goroutine,每个goroutine负责通过调用makeAleph打印一个代码。一旦一个goroutine返回,就会调用wg.Done。总共调用nwg.Done,允许在main中调用wg.Wait完成。

英文:

Your current code is not concurrent at all. All the alphanumeric codes are generated sequentially in a single for loop running in the sole goroutine you are creating besides main.

You are adding len(alphabets) == 26 to wg.Wait. That means you need to call 26 wg.Done for the call to wg.Wait to complete. Each wg.Done call reduces the waitgroup counter by one.

In your code, you are calling wg.Done only once. That means waitgroup counter stays at 25 once your goroutine returns and the call to wg.Wait would never return since no other goroutine is running that may reduce the waitgroup counter by making further calls to wg.Done.

To (vaguely) get what you want, you can try something like this:

// ...
n := 10 // number of codes you want to print
wg.Add(n)
for i := 0; i &lt; n; i++ {
go func(numbers []string, alphabets []string) {
defer wg.Done()
makeAleph(numbers, alphabets)
}(numbers, alphabets)
wg.Wait()
// ...

Now, n goroutines will be spawned, each responsible for printing one code by calling makeAleph. As soon as a goroutine returns, wg.Done is called. A total n wg.Dones are called allowing call to wg.Wait in main to complete.

huangapple
  • 本文由 发表于 2017年5月17日 14:33:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/44017241.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定