Основная функция не ждет чтения канала перед выходом? - PullRequest
0 голосов
/ 24 апреля 2019

Рассмотрим следующую попытку реализации Обеденных философов с подпрограммами и каналами Go.

package main

import "fmt"

func philos(id int, left, right, plate chan bool) {
    fmt.Printf("Philosopher # %d wants to eat\n", id) 
    <-left
    <-right
    plate <- true
    left <- true
    right <- true
    fmt.Printf("Philosopher # %d finished eating\n", id) 
}

func main() {
    const numPhilos = 5 
    var forks [numPhilos]chan bool
    for i := 0; i < numPhilos; i++ {
        forks[i] = make(chan bool, 1)
        forks[i] <- true
    }   
    plates := make(chan bool)
    for i := 0; i < numPhilos; i++ {
        go philos(i, forks[(i-1+numPhilos)%numPhilos], forks[(i+numPhilos)%numPhilos], plates)
    }   
    for i := 0; i < numPhilos; i++ {
        <-plates
    }   
}

Иногда это работает так, как ожидается, то есть все философы едят, например:

Philosopher # 4 wants to eat
Philosopher # 3 wants to eat
Philosopher # 2 wants to eat
Philosopher # 1 wants to eat
Philosopher # 4 finished eating
Philosopher # 3 finished eating
Philosopher # 2 finished eating
Philosopher # 1 finished eating
Philosopher # 0 wants to eat
Philosopher # 0 finished eating

Однако иногда один (или более) философов не хватает (например, Философ # 0, не ел в следующем случае):

Philosopher # 4 wants to eat
Philosopher # 1 wants to eat
Philosopher # 3 wants to eat
Philosopher # 2 wants to eat
Philosopher # 4 finished eating
Philosopher # 0 wants to eat
Philosopher # 2 finished eating
Philosopher # 1 finished eating
Philosopher # 3 finished eating

Вопрос : почему это происходит?

Что я уже знаю:

  1. Программа завершится, если процедура main завершена (даже если какая-то другаяподпрограммы все еще работают).

  2. Подпрограмма go блокируется, если она пытается прочитать данные из канала, и этот канал пуст (т. е. никто не писал в него ранее).

Теперь main пытается прочитать 5 раз из канала plates, поэтому он не должен завершаться, пока подпрограмма philos не будет выполнена пять раз.Но, похоже, ему все равно удается прекратить, прежде чем сделать это.Я что-то пропустил?(Кажется, что plates было прочитано только 4 раза.)

EDIT : Хорошо, подумав немного об этом, я пришел к выводу, что, возможно, philosрутина всегда выполняется 5 раз, однако она может быть прервана до того, как успеет напечатать, что съел философ.Действительно, если я изменю порядок следующим образом, он, кажется, будет работать всегда:

func philos(id int, left, right, plate chan bool) {
    fmt.Printf("Philosopher # %d wants to eat\n", id) 
    <-left
    <-right
    left <- true
    right <- true
    fmt.Printf("Philosopher # %d finished eating\n", id) 
    plate <- true
}

Тем не менее, было бы здорово, если бы кто-то смог подтвердить это объяснение:)

Ответы [ 2 ]

4 голосов
/ 24 апреля 2019

То, что вы видите в stdout, не совпадает с тем, что происходит.Иногда main получает от plates и затем возвращается до того, как произойдет оператор print.Итак:

plate <- true
left <- true    // On this line or on
right <- true   // this line, main receives from plate and then returns before
fmt.Printf("Philosopher # %d finished eating\n", id) // this line executes

Поскольку параллелизм не является детерминированным, это происходит не каждый раз.Иногда печать происходит до того, как main возвращается, иногда - нет.Это не значит, что чтение канала не происходит.

1 голос
/ 24 апреля 2019

На самом деле, канал читается 5 раз, но поскольку основная функция ожидает только чтения канала в 5-й раз, он выходит до того, как функция философии попадет в эту строку:

fmt.Printf("Philosopher # %d finished eating\n", id)`

Для того, чтобы это правильно печаталось, вам нужно запустить эту строку, прежде чем писать в канал планшета.

...