Группа ожидания с каналами

Я играл с этим и придумал:

type Function struct{
    Function func(*TaskGroup, []interface{})
    Args []interface{}
}

type TaskGroup struct{
    Group sync.WaitGroup
    Functions []Function
}

func (x *TaskGroup) Start() {
    for _, Function := range x.Functions{
        x.Group.Add(1)
        go Function.Function(x, Function.Args)
    }
    x.Group.Wait()
}

Чтобы легче работать с несколькими функциями, мне нужно подождать.

Следующие тесты будут работать, и я не понимаю, почему:

func auxC(x *TaskGroup, args []interface{}){
    defer x.Group.Done()
    messageOut := args[0].(chan string)
    messageOut <- "TestC"
}
func auxD(x *TaskGroup, args []interface{}){
    defer x.Group.Done()
    messageOut := args[0].(chan string)
    messageOut <- "TestD"
}

func TestTaskGroupBaseB(t *testing.T) {
    messageC := make(chan string, 1)
    messageD := make(chan string, 1)

    tg := TaskGroup{
        Functions: []Function{
            {auxC, []interface{}{messageC}},
            {auxD, []interface{}{messageD}},
        },
    }
    tg.Start()

    fmt.Println(<- messageC)
    fmt.Println(<- messageD)

    time.Sleep(100 * time.Millisecond)
}

Сначала я попробовал использовать небуферизованные каналы следующим образом:

messageC := make(chan string)
messageD := make(chan string)

Но это не работает, оно просто зависает навсегда, ничего не делая, поэтому у меня есть несколько вопросов:

  1. Почему буферизованные каналы с размером 1 работают, а небуферизованные — нет?
  2. Разве не небуферизованный по умолчанию размер 1?

Рефакторинг кода, см. комментарии:

Главная/Тесты:

func auxC(args []interface{}){
    messageOut := args[0].(chan string)
    messageOut <- "TestC"
}
func auxD(args []interface{}){
    messageOut := args[0].(chan string)
    messageOut <- "TestD"
}

func TestTaskGroupBaseB(t *testing.T) {
    messageC := make(chan string,1)
    messageD := make(chan string,1)

    tg := TaskGroup{
        Functions: []Function{
            {auxC, []interface{}{messageC}},
            {auxD, []interface{}{messageD}},
        },
    }
    tg.Wait()

    fmt.Println(<- messageC)
    fmt.Println(<- messageD)

    time.Sleep(100 * time.Millisecond)
}

Целевая группа:

type Function struct{
    Function func([]interface{})
    Args []interface{}
}

type TaskGroup struct{
    Group sync.WaitGroup
    Functions []Function
}

func (x *TaskGroup) Wait() {
    for _, function := range x.Functions{
        x.Group.Add(1)
        go func(x *TaskGroup, f Function){
            defer x.Group.Done()
            f.Function(f.Args)
        }(x, function)
    }
    x.Group.Wait()
}

person Alpha2k    schedule 22.11.2019    source источник
comment
Почему ubuffered означает буфер размером 1?   -  person JimB    schedule 23.11.2019
comment
Вы читали это? ardanlabs.com/blog/2017/10/the- поведение-оф-каналов.html   -  person jcfollower    schedule 23.11.2019
comment
@jcfollower нет, сделаю, очень хорошо выглядит, спасибо :)   -  person Alpha2k    schedule 23.11.2019
comment
Примечание: Start — не очень хорошее имя для этого исполнителя TaskGroup, потому что он не только запускает все функции, но и ждет, пока все они подадут сигнал о том, что они выполнены. Так что это Do, а не Start.   -  person torek    schedule 23.11.2019
comment
Примечание разработчика: аргумент WaitGroup x передается вызываемым функциям только для того, чтобы они могли вызвать для него Done. (Они больше ничего не будут делать с этим WaitGroup.) Поэтому имеет больше смысла исключить его как аргумент функций, которые вызывает TaskGroup. Пусть TaskGroup выделит каждую функцию через анонимную горутину, которая (1) вызывает функцию, а затем (2) вызывает Done в группе ожидания. Тогда вам вообще не нужно заставлять функции принимать x *TaskGroup в качестве получателя.   -  person torek    schedule 23.11.2019
comment
(Оба вопроса просто в стороне; ДжимБи и Бурак Сердар уже ответили на вопрос о канале.)   -  person torek    schedule 23.11.2019
comment
спасибо @torek, просто красиво, смотрите рефакторинг :)   -  person Alpha2k    schedule 23.11.2019
comment
В обновленном коде либо просто вызовите x.Group.Done() напрямую, либо переместите defer над вызовом f.Function — единственная причина использовать здесь defer — убедиться, что он работает, даже если функция-или-вызов ее сама вызывает панику (и некоторые внешняя система использует восстановление). Чтобы это работало, вы должны отложить вызов Done перед вызовом функции.   -  person torek    schedule 23.11.2019


Ответы (1)


Используя канал с размером буфера 1, сначала записываем данные в буферы, затем заканчиваются горутины и можно читать буферизованные данные в основной горутине.

Когда размер канала равен нулю, запись в канал блокируется до тех пор, пока другая горутина не прочитает его. Итак, обе ваши горутины ждут записи каналов. Если вы переместите вызов Wait() после того, как канал будет прочитан в main, он должен работать.

person Burak Serdar    schedule 22.11.2019