Горутины

Что такое горутина?

Горутина - это функция, которая может выполняться параллельно с другими функциями в одном адресном пространстве. Горутины - это особенность языка программирования Go, который позволяет создавать легковесные потоки исполнения. Горутины могут обмениваться данными с помощью каналов, которые являются потокобезопасными структурами данных.

Источники:

Чем горутина отличается от треда?

Горутина отличается от треда несколькими способами:

  • Горутина имеет меньший размер стека, чем тред, и может динамически его расширять при необходимости.
  • Горутина не связана с конкретным системным потоком, а управляется планировщиком Go, который может переключать горутины между разными потоками.
  • Горутина может быть запущена с помощью ключевого слова go, в то время как тред требует вызова специальной функции или библиотеки.
  • Горутина может общаться с другими горутинами через каналы, которые обеспечивают синхронизацию и безопасность данных. Треды же обычно используют разделяемую память и механизмы блокировки.

Источники:

В чем преимущества горутин над тредами?

Некоторые преимущества горутин над тредами включают:

  • Более высокую производительность и меньшее потребление ресурсов, так как горутины занимают меньше памяти и переключаются быстрее.
  • Более простую и элегантную модель конкурентности, основанную на каналах, которые избегают проблем с блокировками и гонками данных.

Что есть в Golang для многопоточности?

В Golang для многопоточности есть горутины и каналы. Горутины - это легковесные потоки, которые можно запускать с помощью ключевого слова go. Каналы - это потокобезопасные структуры данных, которые позволяют обмениваться данными между горутинами. GOMAXPROCS - это параметр, который определяет, сколько ядер ЦП используется для одновременного выполнения горутин.

Как можно остановить горутину?

Остановить горутину можно с помощью контекста, канала или таймаута. Контекст позволяет передавать сигналы о завершении работы между горутинами. Канал позволяет отправлять и получать значения между горутинами, в том числе команды на остановку. Таймаут позволяет ограничить время работы горутины и прервать ее, если она не успела выполниться.

Вот некоторые примеры использования этих механизмов:

С помощью контекста:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"context"
"fmt"
"time"
)

func main() {
// создаем контекст с отменой
ctx, cancel := context.WithCancel(context.Background())
// запускаем горутину с этим контекстом
go worker(ctx)
// ждем 3 секунды
time.Sleep(3 * time.Second)
// отменяем контекст
cancel()
// ждем еще 2 секунды
time.Sleep(2 * time.Second)
}

func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
// контекст отменен, выходим из горутины
fmt.Println("worker stopped")
return
default:
// продолжаем работать
fmt.Println("worker working")
time.Sleep(time.Second)
}
}
}

С помощью канала:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"fmt"
"time"
)

func main() {
// создаем канал для передачи сигнала остановки
stop := make(chan bool)
// запускаем горутину с этим каналом
go worker(stop)
// ждем 3 секунды
time.Sleep(3 * time.Second)
// отправляем сигнал остановки в канал
stop <- true
// ждем еще 2 секунды
time.Sleep(2 * time.Second)
}

func worker(stop chan bool) {
for {
select {
case <-stop:
// получили сигнал остановки, выходим из горутины
fmt.Println("worker stopped")
return
default:
// продолжаем работать
fmt.Println("worker working")
time.Sleep(time.Second)
}
}
}

С помощью таймаута:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
"fmt"
"time"
)

func main() {
// создаем канал для получения результата работы
result := make(chan string)
// запускаем горутину с этим каналом
go worker(result)
// устанавливаем таймаут в 3 секунды
timeout := time.After(3 * time.Second)
select {
case res := <-result:
// получили результат работы до таймаута
fmt.Println("worker finished:", res)
case <-timeout:
// время вышло, прерываем горутину
fmt.Println("worker timeout")
}
}

func worker(result chan string) {
// имитируем долгую работу
time.Sleep(5 * time.Second)
// отправляем результат в канал
result <- "success"
}

Когда возникает утечка горутины?

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

Некоторые причины утечки горутин могут быть:

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

Вот вам и goroutine

Поделиться