Как устранить тупик канала Go? - PullRequest
0 голосов
/ 24 сентября 2019

Я учусь на языке программирования, в последнее время у меня возникла проблема, я пытаюсь много способов запустить свой код, но я не могу правильно запустить.Как я могу изменить свою программу, чтобы сделать это?

package main

import (
    "fmt"
    "sync"
)

type Task struct {
    Id       int
    Callback chan int
}

func main() {
    var wg sync.WaitGroup
    subTask := make([]Task, 100)
    for i := 0; i < 100; i++ {
        go func(i int) {
            task := Task{
                Id:       i,
                Callback: make(chan int, 1),
            }
            task.Callback <- i
            subTask = append(subTask, task)
        }(i)
    }

    for _, v := range subTask {
        wg.Add(1)
        go func(v Task) {
            defer wg.Done()
            x := <-v.Callback
            fmt.Printf("%d ", x)
        }(v)
    }
    wg.Wait()
}

Ответы [ 4 ]

2 голосов
/ 24 сентября 2019

На subTask есть гонка данных.Программы инициализации задачи читают и записывают переменную subTask без синхронизации.

Цель программы состоит в том, чтобы создать и инициализировать срез 100 Task значений, но он создает срез с нулевым значением 100 Task с и добавляет еще 100 инициализированных Task с (игнорируятолько что упомянутая проблема гонки данных).

Исправьте обе эти проблемы, назначив задачи элементам слайса:

for i := 0; i < 100; i++ {
    go func(i int) {
        task := Task{
            Id:       i,
            Callback: make(chan int, 1),
        }
        task.Callback <- i
        subTask[i] = task
    }(i)
}

На элементах subTask существует гонка данных.Нет никакой гарантии, что процедуры инициализации задачи завершат запись в элементы до того, как основная программа будет охватывать эти элементы.Исправьте с помощью группы ожидания, чтобы скоординировать завершение инициализационных и основных процедур:

subTask := make([]Task, 100)
for i := 0; i < 100; i++ {
    wg.Add(1)
    go func(i int) {
        task := Task{
            Id:       i,
            Callback: make(chan int, 1),
        }
        task.Callback <- i
        subTask[i] = task
        wg.Done()
    }(i)
}
wg.Wait()

Запустите код на игровой площадке .

детектор расы сообщает обо всех расах, упомянутых выше.

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

0 голосов
/ 24 сентября 2019

Вместо части задач вы могли бы рассмотреть чан задач.

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

func main() {
    subTasks := make(chan Task)
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            task := Task{i, make(chan int)}
            subTasks <- task
            task.Callback <- i
        }(i)
    }

    go func() {
        wg.Wait()
        close(subTasks)
    }()

    for v := range subTasks {
        go func(v Task) {
            fmt.Println(<-v.Callback)
        }(v)
    }
}

Бег на детской площадке

0 голосов
/ 24 сентября 2019

Одной из проблем является то, что вы добавляете к срезу вместо обновления существующих элементов среза.Также вам не нужен буферизованный канал.

func main() {
    subTask := make([]Task, 100)
    for i := range subTask {
        go func(i int) {
            subTask[i] = Task{i, make(chan int)}
            subTask[i].Callback <- i
        }(i)
    }

    var wg sync.WaitGroup
    wg.Add(len(subTask))
    for _, v := range subTask {
        go func(v Task) {
            defer wg.Done()
            fmt.Println(<-v.Callback)
        }(v)
    }
    wg.Wait()
}
0 голосов
/ 24 сентября 2019

Если канал равен нулю, <-c получает от блоков c навсегда.отсюда и тупик. Причина, по которой канал может быть равен нулю, заключается в том, что одна из последовательностей 1-го цикла for может не выполняться во время выполнения приема подпрограммы.

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

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

Еще одна вещь, которая может быть проблемой, это subTask := make([]Task, 100) Это утверждение создает 100 пустых задач в объекте.slice и append добавляет к нему больше, поэтому длина в конечном итоге увеличивается до 200.

https://play.golang.org/p/4bZDJ2zvKdF

...