Использование goroutines

Мы рассмотрим несколько примеров запуска goroutine и контроля их выполнения.

В golang есть отличный механизм для запуска параллельных потоков например:

package main

import (
	"log"
	"time"
)

func job(duration time.Duration)  {
    <-time.After(duration)
     log.Println("job done") 
}

func main() {
    go func(){
		job(time.Minute)
    }() 
}

Но как и любой механизм он не лишен недостатков, предыдущий пример может не завершить работу в goroutine при этом основной поток уже будет остановлен.

Нам уже необходимо будет ожидать выполнение процесса например при помощи `WaitGroup`

package main

import (
	"log"
	"sync"
	"time"
)

func main() {
	wg := sync.WaitGroup{}
	
	wg.Add(1)
	go func() {
		defer wg.Done()
		job(time.Microsecond)
	}()
	wg.Wait()
}

А что если нам надо чтобы процесс был запущен много раз, тогда получается

package main

import (
	"log"
	"time"
)

func main()  {
 	go func() {
 		for{
 			job(time.Minute)
 		}
 	}()
}

В примерах я буду показывать только ключевые моменты, посмотреть пример полностью можно будет в Go Playground по ссылке.

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

package main

import (
	"context"
	"log"
	"time"
)


func main()  {
	ctx, cancel := context.WithTimeout(context.Background(),time.Microsecond)
	defer cancel()
 	go func() {
 		for{
 			select{
 			case <-ctx.Done():
 				return
 			default:
 				job(time.Microsecond/2)
 			}
 		}
 	}()
}

В этом случае мы управляем процессом с помощью пакета `context`, он может быть остановлен к примеру `graceful shutdown` но об этом в следующий раз.

Эта работа будет запускаться всегда как только завершится предыдущая, это встречается редко, обычно нам надо запускать обработку не чаще чем, изменим код и получится

package main

import (
	"context"
	"log"
	"time"
)


func main()  {
 	go func() {
 		for{
 			select{
 			case <-time.After(time.Microsecond):
 				job(time.Microsecond/2)
 			}
 		}
 	}()
}

Выводы

Я привел несколько примеров как просто запускать выполнение фоновых операций параллельно, но это не все что может потребоваться. Обычно это вырастает в каждом проекте по своему, ведь это просто, потом разрастается ведь нам к примеру нужны будут еще метрики и запись логов, ведь процесс фоновый. Чтобы избежать повторения базового решения можно использовать пакет go-4devs/daemon, который уже все это делает и даже больше. Включая обработку завершения работ(jobs), завершения всех запущенных job и тп.

Читайте также:

Разворачивание golang приложения с помощью gitlab-ci и docker swarm

У меня есть несколько своих проектов, которые соответственно приходится самому поддерживать разворачивать и тп, к примеру данный блог и другие простые проекты. Когда я использовал symfony и php, развернуть можно просто с помощью capifony, даже не зная Ruby. В связи с тем что я решил перейти на golang то данные инструменты не совсем подходят. Почему не использовать к примеру в проектах тот же php, чаще быстрее разрабатывать на том что используешь в работе. Для начала решил попробовать обычный способ с помощью docker swarm и gitlab ci, есть конечно решения с heroku, но я пока решил использовать vps например vscale. K8s для маленьких проектов возможно будет излишним.

Введение в go mod

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

Объединение строк в golang

Одной из частых операций может оказаться объединения(concatenation) строк, есть много библиотек для решения задач, мы рассмотрим несколько самых распространенных примеров.