如何避免同时打印字母数字的死锁

时间:2017-05-17 06:33:27

标签: go concurrency deadlock goroutine

我是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"}

    //var aleph alphanumeric
    //var alephS []alphanumeric

    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()
} // 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()]

}

正确打印所需数字后出现的错误是:

❯ 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

如何在同时打印字母数字时避免死锁?

1 个答案:

答案 0 :(得分:0)

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

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

在您的代码中,您只调用wg.Done一次。这意味着一旦你的goroutine返回并且对wg.Wait的调用永远不会返回,则waitgroup计数器将保持在25,因为没有其他goroutine正在运行,这可能会通过进一步调用wg.Done来减少waitgroup计数器。

(模糊地)得到你想要的东西,你可以尝试这样的东西:

// ...

n := 10 // number of codes you want to print

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个goroutines,每个goroutine都会通过调用makeAleph来打印一个代码。一旦goroutine返回,就会调用wg.Done。共调用n wg.Donewg.Wait,以便main中的{{1}}完成调用。