Irbisa · Шпаргалка
Горутины и каналы
Каналы, select, пулы воркеров, утечки горутин, errgroup
Что такое горутина и чем она отличается от потока ОС?
Чем конкурентность отличается от параллелизма?
Что происходит с горутинами, которые ещё работают, когда
mainвозвращается, и как заставитьmainих дождаться?func main() { var wg sync.WaitGroup for i := range 3 { wg.Add(1) // before `go`, in the parent go func() { defer wg.Done()…Как получить результат из горутины, если
go f()выбрасывает возвращаемые значения?func squares(nums []int) []int { results := make([]int, len(nums)) var wg sync.WaitGroup for i, n := range nums { wg.Add(1) go func() {…Чем буферизованный канал отличается от небуферизованного и когда блокируются отправка и приём у каждого из них?
func main() { unbuf := make(chan int) go func() { unbuf <- 1 }() // waits until main receives fmt.Println(<-unbuf) // 1 buf := make(chan int, 2)…Что происходит при отправке, приёме и закрытии для закрытого канала и что происходит для nil-канала?
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 close(ch)…Как работает
select: что происходит, когда готовы несколько кейсов, зачем нуженdefaultи как добавить таймаут?func receive(ctx context.Context, ch <-chan Msg) (Msg, error) { select { case m := <-ch: return m, nil case <-time.After(time.Second): return Msg{}, errors.New("timeout")…Что напечатает эта программа и почему рантайм сообщает о дедлоке здесь, но молчит в зависшем продакшн-сервисе?
func main() { ch := make(chan int, 3) for i := 1; i <= 3; i++ { ch <- i } for v := range ch { fmt.Println(v) } fmt.Println("done")}Кто должен закрывать канал и что дают направленные типы
<-chan Tиchan<- T?// The stage owns its output: it creates it, sends on it and closes it.func square(in <-chan int) <-chan int { out := make(chan int) go func() { defer close(out) // the only sender closes, once for v := range in {…Эта программа должна напечатать 1000. Что она напечатает на самом деле и как это исправить?
func main() { var count int var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() count++ }() } wg.Wait() fmt.Println(count)}Найдите баг:
fetchAllвозвращает пустые строки для страниц, которые загрузились без ошибок.func fetchAll(urls []string) []string { var wg sync.WaitGroup bodies := make([]string, len(urls)) for i, u := range urls { go func() { wg.Add(1) defer wg.Done() bodies[i] = fetch(u) }() } wg.Wait() return bodies}Как канал устроен в рантайме и что происходит с горутиной, которая заблокировалась на отправке?
Когда использовать канал, а когда мьютекс, и почему «делитесь памятью через общение» не всегда правильный ответ?
Что такое утечка горутин, что обычно её вызывает и как её обнаружить?
// wrong: on timeout the handler returns and the worker parks in its send foreverfunc handle(w http.ResponseWriter, r *http.Request) { out := make(chan Result) // right: make(chan Result, 1) go func() { out <- compute(r.Context()) }() select {…Чем различаются
time.After,time.NewTimer,time.NewTickerиtime.AfterFuncи что поменялось в таймерах в Go 1.23?func relay(ctx context.Context, in <-chan Msg) error { const idle = 5 * time.Minute t := time.NewTimer(idle) // one timer for the whole loop defer t.Stop() for {…Как реализовать пул воркеров на Go, собрать результаты и аккуратно остановить воркеров?
func process(ctx context.Context, jobs []Job, n int) []Result { in := make(chan Job) out := make(chan Result) var wg sync.WaitGroup for range n {…Как ограничить число одновременно работающих горутин и чем это отличается от пула воркеров?
func uploadAll(ctx context.Context, items []Item) error { sem := make(chan struct{}, 8) var wg sync.WaitGroup for _, it := range items { select { case sem <- struct{}{}: // acquire before go: the loop waits here…Как слить N каналов в один (fan-in) и что будет, если один из входов никогда не закроется?
func Merge[T any](ctx context.Context, ins ...<-chan T) <-chan T { out := make(chan T) var wg sync.WaitGroup for _, in := range ins { wg.Go(func() { for {…Напишите две горутины, которые по очереди печатают числа от 1 до N: одна печатает нечётные, а другая чётные.
func player(from, n int, mine <-chan struct{}, next chan<- struct{}) { for i := from; i <= n; i += 2 { <-mine // wait for our turn fmt.Println(i) if i < n { // nobody is left to take the turn after the last number next <- struct{}{}…Как запустить N задач параллельно, отменить остальные при первой ошибке и дождаться всех?
func fetchAll(ctx context.Context, urls []string) ([]Page, error) { g, ctx := errgroup.WithContext(ctx) g.SetLimit(16) // once, before the first Go pages := make([]Page, len(urls)) for i, u := range urls {…Как реализовать потокобезопасный rate limiter и когда вместо этого брать
golang.org/x/time/rate?type Bucket struct { mu sync.Mutex tokens float64 burst float64 rate float64 // tokens per second last time.Time…Как написать потокобезопасный in-memory кэш с TTL, не запуская горутину на каждый ключ?
type entry struct { val string expires time.Time} type Cache struct {…Как построить пайплайн из стадий на каналах и как стадии завершаются, когда потребитель перестаёт читать раньше времени?
func gen(ctx context.Context, nums ...int) <-chan int { out := make(chan int) go func() { defer close(out) for _, n := range nums { select {…Что такое backpressure в пайплайне на каналах, что на самом деле даёт буфер и что делать, когда потребитель стабильно медленнее?
Параллельные воркеры ускорили стадию, но перемешали порядок на выходе. Как восстановить порядок входа, не держа весь поток в памяти?
type job struct { row Row res chan Result // cap 1: the worker never waits for a slow consumer} func enrich(rows []Row, n int) <-chan Result {…