2017-01-20 2 views
0

Я пишу этот примерный код, чтобы научить себя, как делиться каналами через параллельные goroutines, и я попадаю в состояние гонки. Программа должна запускать как можно больше goroutines, так как в системе есть доступные процессоры. Первый goroutine для доступа к каналу bl немедленно устанавливает, что канал содержит false, так что никакие другие goroutines не могут получить доступ к циклу, который находится по каналу st. Остальные goroutines должны заканчиваться как первая goroutine, которая обратилась к каналу bl, считывает с канала st и печатает каждое значение.Состояние голангского гонорара при совместном использовании каналов через параллельные goroutines

package main 

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

func main() { 
    runtime.GOMAXPROCS(runtime.NumCPU()) 
    var wg sync.WaitGroup 
    st := make(chan string) 
    bl := make(chan bool) 
    go func() {bl <- true}() 

    for i := 0; i < runtime.NumCPU(); i++ { 
     wg.Add(1) 
     go func(x int) { 
      defer wg.Done() 
      fmt.Println("me: ", strconv.Itoa(x)) 

      can := <- bl 
      bl <- false 

      if can { 
       for val := range st { 
        t  := strconv.Itoa(rand.Int()%3)+"s" 
        dur, _ := time.ParseDuration(t) 
        time.Sleep(dur) 

         fmt.Println("time: ", t," i: ", strconv.Itoa(x), val) 
       } 
      } 
      fmt.Println("done: ", strconv.Itoa(x)) 
     }(i) 
    } 

    for i := 0; i < 10; i++ { 
     st <- "n: "+strconv.Itoa(i) 
    } 

    wg.Wait() 
    close(st) 
} 

Когда я запускаю код, я получаю следующее сообщение об ошибке:

$ go run share.go 
me: 1 
me: 0 
me: 2 
me: 3 
done: 0 
done: 2 
time: 2s i: 1 n: 0 
time: 1s i: 1 n: 1 
time: 0s i: 1 n: 2 
time: 2s i: 1 n: 3 
time: 2s i: 1 n: 4 
time: 2s i: 1 n: 5 
time: 0s i: 1 n: 6 
time: 2s i: 1 n: 7 
time: 2s i: 1 n: 8 
time: 2s i: 1 n: 9 
fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [semacquire]: 
sync.runtime_Semacquire(0xc4200701bc) 
    /usr/local/go/src/runtime/sema.go:47 +0x30 
sync.(*WaitGroup).Wait(0xc4200701b0) 
    /usr/local/go/src/sync/waitgroup.go:131 +0x97 
main.main() 
    /share.go:80 +0x1ef 

goroutine 36 [chan receive]: 
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x1) 
    /share.go:64 +0x23e 
created by main.main 
    /share.go:73 +0x127 

goroutine 38 [chan send]: 
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x3) 
    /share.go:61 +0x1ef 
created by main.main 
    /share.go:73 +0x127 
exit status 2 

Я не знаю, как справиться с этим состоянием гонки для канала бл. Кажется, что последний goroutine застрял, пытаясь прочитать из канала bl, но нет ничего, чтобы читать это, потому что goroutine перед ним еще не вставил false. Не уверен, что моя интуиция верна. Я разместил строки печати вокруг кода, и это, по-видимому, происходит по крайней мере. Я также попытался установить if, если вокруг канала bl, но это закончилось тем же вопросом. Я также переместил bl <- false внутри блока if can {, но это тоже не сработало. Как это исправить?

ответ

1

Вы можете отправить канал по каналу для защиты единого доступа к выбранному значению. Только получатель выполнит работу, а остальные уйдут, когда они получат сигнал закрытия. См. this solution

+0

Проблема с вашим текущим решением заключается в том, что последний goroutine при отправке по каналу b1 будет ожидать, что получатель получит отправленное значение, так что goroutine никогда не выйдет, ни главная горутин, которая ждет целого группа горти. Он может быть исправлен с использованием буферизованного канала (b1: = make (chan bool, 1)), когда 1 является размером буфера канала. Это заставит ваш подход работать, но я думаю, что мое предлагаемое решение более подробно объясняет, почему значения передаются по каналам –

+0

Awesome! Спасибо за пример. Это действительно помогло. – Soubriquet

Смежные вопросы