Concurrency patterns

Pipeline(конвейер)

Каналы могут использоваться для подключения go-подпрограмм так, чтобы выход одной из них был входом для другой. Это называется конвейером (pipeline).

func main() {
	naturals := make(chan int)
	squares := make(chan int)
    // Генерация
    go func() {
        for x := 0; ; x++ {
            naturals <-x
        }
    }()
    // Возведение в квадрат
    go func() {
        for {
            x := <-naturals
            squares <- x * x
        }
    }()
    // Вывод (в главной go-подпрограмме)
    for {
    	fmt.Println(<-squares)
    }
}

Broadcasting

В go нет возможности полноценной широковещательной рассылки нескольким получателям, однако есть лайфхак: при закрытии канала, все консюммеры, могут фиксировать это событие(закрытие канала)

Cancelation

Чтобы отменить выполнение горутины в определенный момент, можем использовать канал отмены:

func main() {
	var cancelChan chan bool
	cancelChan  = make(chan bool)

	go func() {
		for {
			select {
			case <-cancelChan:
				fmt.Println("Получили сигнал, отмены, вышли")
				return
			default:
				fmt.Println("Делаем полезную работу")
			}
			time.Sleep(3)

		}
	}()

	time.Sleep(10)
	cancelChan <- true
}

Promises

В информатике конструкции future, promise и delay в некоторых языках программирования формируют стратегию вычисления, применяемую для параллельных вычислений. С их помощью описывается объект, к которому можно обратиться за результатом, вычисление которого может быть не завершено на данный момент. Использование future может быть неявным (при любом обращении к future возвращается ссылка на значение) и явным (пользователь должен вызвать функцию, чтобы получить значение). Получение значения из явного future называют stinging или forcing. Явные future могут быть реализованы в качестве библиотеки, в то время, как неявные обычно реализуются как часть языка.

package main

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

type Promise struct {
	wg  sync.WaitGroup
	res string
	err error
}

func NewPromise(f func() (string, error)) *Promise {
	p := &Promise{}
	p.wg.Add(1)
	go func() {
		p.res, p.err = f()
		p.wg.Done()
	}()
	return p
}

func (p *Promise) Then(r func(string), e func(error)) {
	go func() {
		p.wg.Wait()
		if p.err != nil {
			e(p.err)
			return
		}
		r(p.res)
	}()
}

func exampleTicker() (string, error) {
	<-time.Tick(time.Second * 1)
	return "hi", nil
}

func main() {
	doneChan := make(chan int)
	var p = NewPromise(exampleTicker)
	p.Then(func(result string) { fmt.Println(result); doneChan <- 1 }, func(err error) { fmt.Println(err) })
	<-doneChan
}

Generator aka Iterator

Futures/Promises

Fan-in, Fan-out