Тупик в программе Houseie. Модель производитель-потребитель

Я пытаюсь реализовать домашнюю игру, в которой горутина производит числа, 3 других горутины проверяют, есть ли они в их токенах, и сообщают производителю, были ли произведены все их числа. Я реализовал это в golang следующим образом. Это приводит к тупику. Любая идея, почему это происходит? Это «домашнее задание», я просто реализую его, чтобы научиться работать лучше.

package main

import (
    "fmt"
    "math/rand"
)

type PersonID int

func contains(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

func Person(called_number chan int, claim_prize chan PersonID, received chan bool, coupon []int, person_id PersonID) {
    numFound := 0
    for i := 0; i < len(coupon); i++ {
        current_number := <-called_number
        found := contains(coupon, current_number)
        if found {
            numFound++
        }
        if numFound == len(coupon) {
            claim_prize <- person_id
        } else {
            received <- true
        }
    }
}

func main() {
    var called_number chan int
    var claim_prize chan PersonID
    var received chan bool

    tokens := make([][]int, 3)
    for i := 0; i < 3; i++ {
        tokens[i] = make([]int, 12)
        for j := 0; j < 12; j++ {
            num := rand.Intn(100) + 1
            found := contains(tokens[i], num)
            for found {
                num = rand.Intn(100) + 1
                found = contains(tokens[i], num)
            }
            tokens[i][j] = num
        }
    }

    go Person(called_number, claim_prize, received, tokens[0], 0)
    go Person(called_number, claim_prize, received, tokens[1], 1)
    go Person(called_number, claim_prize, received, tokens[2], 2)

    claimants := make([]PersonID, 0)
    prev_called := make(map[int]bool)
    for i := 0; i < 100; i++ {
        if len(claimants) == 3 {
            break
        }
        num := rand.Intn(100) + 1
        _, ok := prev_called[num]
        for ok {
            num = rand.Intn(100) + 1
            _, ok = prev_called[num]
        }
        prev_called[num] = true
        called_number <- num
        for j := 0; j < 3; j++ {
            select {
            case _ = <-received:
                continue
            case pid := <-claim_prize:
                claimants = append(claimants, pid)
            }
        }
    }

    fmt.Println(claimants)
}

РЕДАКТИРОВАТЬ: Точная проблема заключается в том, что производитель должен отправить номер каждому из потребителей. Когда потребитель получает все числа в своем токене, он может претендовать на приз. Основываясь на том, что сказал @OneOfOne, я внес некоторые изменения в программу. Изменения заключаются в том, что теперь для каждого из потребителей есть отдельный канал, и я закрываю его после того, как он требует приз. Внизу новая программа, она по-прежнему тупиковая.

package main

import (
    "fmt"
    "math/rand"
)

func contains(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

func Person(called_number chan int, claim_prize chan int, received chan bool, coupon []int, person_id int) {
    numFound := 0
    for current_number := range called_number {
        if contains(coupon, current_number) {
            numFound++
        }
        if numFound == len(coupon) {
            fmt.Println(person_id)
            claim_prize <- person_id
        } else {
            received <- true
        }
    }
}

func main() {
    var (
        called_number1 = make(chan int, 1)
        called_number2 = make(chan int, 1)
        called_number3 = make(chan int, 1)
        claim_prize    = make(chan int, 1)
        received       = make(chan bool, 1)
    )

    tokens := make([][]int, 3)
    for i := 0; i < 3; i++ {
        tokens[i] = make([]int, 12)
        for j := 0; j < 12; j++ {
            num := rand.Intn(100) + 1
            found := contains(tokens[i], num)
            for found {
                num = rand.Intn(100) + 1
                found = contains(tokens[i], num)
            }
            tokens[i][j] = num
        }
    }

    go Person(called_number1, claim_prize, received, tokens[0], 0)
    go Person(called_number2, claim_prize, received, tokens[1], 1)
    go Person(called_number3, claim_prize, received, tokens[2], 2)

    claimants := make([]int, 0)
    prev_called := make(map[int]bool)
    for i := 0; i < 100; i++ {
        if len(claimants) == 3 {
            break
        }
        num := rand.Intn(100) + 1
        _, ok := prev_called[num]
        for ok {
            num = rand.Intn(100) + 1
            _, ok = prev_called[num]
        }
        prev_called[num] = true
        if !contains(claimants, 0) {
            called_number1 <- num
        }
        if !contains(claimants, 1) {
            called_number2 <- num
        }
        if !contains(claimants, 2) {
            called_number3 <- num
        }
        for j := 0; j < 3; j++ {
            select {
            case _ = <-received:
                continue
            case pid := <-claim_prize:
                if pid == 0 { close(called_number1) }
                if pid == 1 { close(called_number2) }
                if pid == 2 { close(called_number3) }
                claimants = append(claimants, pid)
            }
        }
    }
    fmt.Println(claimants)
}

EDIT2: Это все еще зашло в тупик, потому что я не уменьшал количество каналов для ожидания даже после завершения горутин. Сделал так и все работает.


person Sacheendra Talluri    schedule 11.04.2015    source источник


Ответы (1)


Несколько проблем:

  1. Вы используете нулевой канал, поэтому он просто блокируется навсегда, по какой-то причине это не паника.

  2. Кроме того, ваш второй внутренний цикл будет блокироваться на неопределенный срок, потому что он ожидает чтения, но ничего не отправляется.

  3. После того, как цикл Person завершится, called_number <- num заблокируется навсегда.

//редактируем рабочий код Kinda: http://play.golang.org/p/3At5nuJTuk

Но логика ущербна, придется переосмыслить.

person OneOfOne    schedule 11.04.2015
comment
Мне нужно было закрыть каналы и уменьшить количество ответов, которые нужно ждать после завершения выполнения некоторых горутин человека. Спасибо вам за помощь. - person Sacheendra Talluri; 12.04.2015