Как реализовать очередь выполнения goroutines?

День добрый! Задача требует реализовать очередь выполнения запросов.
В общем случае ситуация следующая. Веб-сервис принимает json массив функций которые необходимо выполнить. Сам массив очень похож на JSON-RPC только с добавлением еще одного свойства wait

Вот пример
[
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    }
]



Набросал пример реализации ожидания. За "красивость" кода не судите строго, это просто пример.

Вот он
package main

import (
	"fmt"
	"sync"
	"time"
)


type chain struct {
	Req       *req
	Wait      chan bool
	Signal    chan bool
	IsWaiting bool
}

type req struct {
	Method func()
	ID     int
	W      int
}

func main() {

	chainigs := make(map[int]*chain)

	var fncs []*req

	r1 := &req{
		Method: func1,
		ID:     1,
		W:      0,
	}

	r2 := &req{
		Method: func2,
		ID:     2,
		W:      1,
	}
	r3 := &req{
		Method: func3,
		ID:     3,
		W:      0,
	}

	fncs = append(fncs, r1, r2, r3)

	for _, req := range fncs {

		ch := &chain{}

		ch.Req = req

		signalChan := make(chan bool)

		ch.Signal = signalChan

		chainigs[req.ID] = ch

		if waitChan, ok := chainigs[req.W]; ok {
			ch.Wait = waitChan.Signal
			ch.IsWaiting = true
		} else {
			ch.IsWaiting = false
		}

		chainigs[req.ID] = ch
	}

	var wg sync.WaitGroup

	for _, schain := range chainigs {

		wg.Add(1)

		go func(c *chain) {

			if c.IsWaiting {
				select {
				case <-c.Wait:
					c.Req.Method()
					wg.Done()
					go func() {
						c.Signal <- true
						close(c.Signal)
					}()
				}
			} else {
				c.Req.Method()
				wg.Done()
				c.Signal <- true
			}
		}(schain)
	}

	wg.Wait()
}

func func1() {
	time.Sleep(2 * time.Second)
	fmt.Println("FORM 1")
}

func func2() {
	fmt.Println("FROM 2")
}

func func3() {
	time.Sleep(2 * time.Second)
	fmt.Println("FROM 3")
}



Идея вот в чем. Структура chain имеет два поля с каналами Wait и Signal.

Wait - сдержит канал Signal другой структуры, выполение которой нужно ожидать. Ну а дальше запуск в цикле гороутин, и проверяем, если chain должен ожидать выполнение другого, то ждем сигнала. Если нет, то просто выполняем функцию и посылаем завершающий сигнал. При такой реализации удается строить цепочки вызовов, и в целом программа работает правильно. Меня интересует насколько правильным можно считать такой подход? Возможно, есть паттерны намного лучше и красивей для реализации такой возможности?
  • Вопрос задан
  • 570 просмотров
Решения вопроса 1
Может вам удобнее навесить вейтгруппы на процессы и запустить их все одновременно?
https://golang.org/pkg/sync/#WaitGroup

1. Каждой задаче в структуру пихаем вейтгруппу, делаем ей Add(1), а после выполнения делаем Done().
2. У каждой зависимой задачи перед выполнением делаем Wait() у вейтгрупп тех задач, которые она должна дождаться.
3. ????
4. PROFIT!!!
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@RidgeA
не знаю как лучше, но я бы это отсортировал в порядке зависимостией и поочереди выполнял бы методы.

[
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }
]


привести к виду
[
  [
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }  
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }  
  ]
]


а потом уже как-то так
for _, step := range steps {
  for _, f : range step {
    f.method(f.params)
  }
}


что-то похоже на на графы и топологическую сортировку
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы