Я играл с этим а> и придумал:
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?
Рефакторинг кода, см. комментарии:
Главная/Тесты:
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()
}
Start
— не очень хорошее имя для этого исполнителяTaskGroup
, потому что он не только запускает все функции, но и ждет, пока все они подадут сигнал о том, что они выполнены. Так что этоDo
, а неStart
. - person torek   schedule 23.11.2019WaitGroup
x
передается вызываемым функциям только для того, чтобы они могли вызвать для негоDone
. (Они больше ничего не будут делать с этимWaitGroup
.) Поэтому имеет больше смысла исключить его как аргумент функций, которые вызываетTaskGroup
. ПустьTaskGroup
выделит каждую функцию через анонимную горутину, которая (1) вызывает функцию, а затем (2) вызываетDone
в группе ожидания. Тогда вам вообще не нужно заставлять функции приниматьx *TaskGroup
в качестве получателя. - person torek   schedule 23.11.2019x.Group.Done()
напрямую, либо переместитеdefer
над вызовомf.Function
— единственная причина использовать здесьdefer
— убедиться, что он работает, даже если функция-или-вызов ее сама вызывает панику (и некоторые внешняя система использует восстановление). Чтобы это работало, вы должны отложить вызовDone
перед вызовом функции. - person torek   schedule 23.11.2019