Неожиданное поведение Goroutine - PullRequest
0 голосов
/ 17 января 2019

Я новичок в Голанге

Я читал о параллелизме в Go с здесь.

Все шло хорошо, пока мне не поставили вопрос на 8-м слайде.
Вопрос в том, чтобы выяснить, эквивалентны ли два заданных двоичных дерева.
Мой подход: сделать обход Inorder, сохранить значения из обоих деревьев в срез и сравнить их.

Вот мое решение: [неполное]

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t != nil {
        Walk(t.Left, ch)
        ch <- t.Value
        Walk(t.Right, ch)
    }
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        fmt.Println("executing first go routing")
        Walk(t1, ch1)
        fmt.Println("closing channel [ch1]")
        close(ch1)
    }()

    go func() {
        fmt.Println("executing second go routing")
        Walk( t2, ch2 )
        fmt.Println("closing channel [ch2]")
        close(ch2)
    }()

    shouldContinue := true
    var continue1, continue2 bool
    for shouldContinue {
        select {
        case r1, ok1 := <-ch1:
            fmt.Println("[ch1] [rcvd]", r1)
            continue1 = ok1

        case r2, ok2 := <-ch2:
            fmt.Println("[ch2] [rcvd]", r2)
            continue2 = ok2
        }
        shouldContinue = continue1 || continue2
    }
    return true
}

func main() {
    Same(tree.New(1), tree.New(1))
}

Я знаю, что программы распланированы совместно, и одна полностью блокирует другую, если она постоянно или выполняет вычисления. Таким образом, я ожидал, что для вывода он сначала получит значения из любого из каналов, закроет его, а затем получит значения из другого канала, а затем закроет. Как только оба будут закрыты, цикл for прекратится.

К моему удивлению, рутина первого хода никогда не назначается. Вот вывод, который я получаю:

executing second go routing
[ch2] [rcvd] 1
[ch2] [rcvd] 2
[ch2] [rcvd] 3
[ch2] [rcvd] 4
[ch2] [rcvd] 5
[ch2] [rcvd] 6
[ch2] [rcvd] 7
[ch2] [rcvd] 8
[ch2] [rcvd] 9
[ch2] [rcvd] 10
closing channel [ch2]
[ch2] [rcvd] 0 

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

Любая помощь будет оценена. Спасибо.

UPDATE:
Я погуглил о выходе из каналов и нашел ТАК вопрос здесь. Согласно которому я обновил свое решение следующим образом:

package main

import (
    "fmt"
    "golang.org/x/tour/tree"
    // "time"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    // time.Sleep(time.Millisecond)
    if t != nil {
        Walk(t.Left, ch)
        ch <- t.Value
        Walk(t.Right, ch)
    }
}

// Same determines whether the trees
// t1 and t2 contain the same values.
func Same(t1, t2 *tree.Tree) bool {
    ch1 := make(chan int)
    ch2 := make(chan int)

    go func() {
        fmt.Println("executing first go routing")
        Walk(t1, ch1)
        fmt.Println("closing channel [ch1]")
        close(ch1)
    }()

    go func() {
        fmt.Println("executing second go routing")
        Walk( t2, ch2 )
        fmt.Println("closing channel [ch2]")
        close(ch2)
    }()

    for {
        select {
        case r1, ok1 := <-ch1:
            fmt.Println("[ch1] [rcvd]", r1)
            if !ok1 {
                ch1 = nil
            } 

        case r2, ok2 := <-ch2:
            fmt.Println("[ch2] [rcvd]", r2)
            if !ok2 {
                ch2 = nil
            }
        }
        if ch1 == nil && ch2 == nil {
            break
        }
    }
    return true
}

func main() {
    Same(tree.New(1), tree.New(1))
}

Что дает точный результат, который я думал, что первый фрагмент будет:

executing second go routing
[ch2] [rcvd] 1
[ch2] [rcvd] 2
[ch2] [rcvd] 3
[ch2] [rcvd] 4
[ch2] [rcvd] 5
[ch2] [rcvd] 6
[ch2] [rcvd] 7
[ch2] [rcvd] 8
[ch2] [rcvd] 9
[ch2] [rcvd] 10
closing channel [ch2]
[ch2] [rcvd] 0
executing first go routing
[ch1] [rcvd] 1
[ch1] [rcvd] 2
[ch1] [rcvd] 3
[ch1] [rcvd] 4
[ch1] [rcvd] 5
[ch1] [rcvd] 6
[ch1] [rcvd] 7
[ch1] [rcvd] 8
[ch1] [rcvd] 9
[ch1] [rcvd] 10
closing channel [ch1]
[ch1] [rcvd] 0

Теперь я еще больше запутался в происходящем.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

В первой части вашего кода есть ошибка в вашей логике.

shouldContinue := true
var continue1, continue2 bool
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}

В приведенном выше коде continue1 и continue2 равны false. select блокируется, пока не будет выполнено одно из его дел. Допустим, сначала выполняется case r2, ok2 := <-ch2:, затем continue2 будет true. Из-за shouldContinue = continue1 || continue2 этого условия цикл for продолжится. По какой-то причине (иди рутинное планирование) case r2, ok2 := <-ch2: условие выполняется каждый раз. Теперь, когда ch2 закрывается, значение ok2 будет false, поэтому continue2 также будет false. Теперь continue1 и continue2 равны false, поэтому shouldContinue также будет false. Таким образом, он прерывает цикл for, и вы не можете видеть результат от ch1. Попробуйте вместо этого:

continue1 = true
continue2 = true
for shouldContinue {
    select {
    case r1, ok1 := <-ch1:
        fmt.Println("[ch1] [rcvd]", r1)
        continue1 = ok1

    case r2, ok2 := <-ch2:
        fmt.Println("[ch2] [rcvd]", r2)
        continue2 = ok2
    }
    shouldContinue = continue1 || continue2
}

Когда канал закрыт, вы не можете отправить значение на этот канал, но вы все равно можете получить его из канала. Смотрите здесь: https://play.golang.org/p/S4USguWfN_z.

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

0 голосов
/ 17 января 2019

Когда канал 2 закрыт, почему не выполняется первый канал?

Каналы не выполняются.То, что исполняется снова и снова, является вашим выбором.Обратите внимание, что оба случая могут выполняться всегда, независимо от того, закрыт канал или нет.Так что можно выбрать для выбора второй случай, который сделал идентификатор, и вы прервали.(Ваше условие прерывания выглядит подозрительно: вы сделали один раз оба каналы закрыты, т. Е. Если оба ok1 и ok2 ложные).

Не думайте о выборе как о"средство планирования goroutine" само по себе.Это не.Он случайным образом выберет один из запускаемых случаев.Если все ваши дела имеют форму val, ok := <- ch, тогда все они доступны и выберите всегда, можно выбрать второе.Или первое, или ...

[Второе решение] Теперь я еще больше запутался в происходящем.

Вы прерваны.Вы обрываетесь, когда оба канала равны нулю, что происходит после закрытия обоих каналов.Это отличается от вашего первого решения, потому что первое прерывание происходит один раз любой один канал закрывается.

Проблема с параллелизмом здесь не в расписании рутин, а исключительно в прерыванииусловие вашего цикла for делает выбор.Они отличаются от первого и второго, и первый в корне неверен, так как останавливается после исчерпания любого канала.

...