Golang, горутины и каналы

горутины и каналы играют важную роль в конкуренции, а также являются основным преимуществом Golang. Я надеюсь, что вы пришли сюда после того, как хорошо поняли, как работает Golang. Давайте начнем прямо сейчас 🙂
Что такое Go-горутины?
Горутина — это функция, которая может работать параллельно с другими функциями. Для создания горутины используется ключевое слово go , за которым следует вызов функции. Эта программа состоит из двух горутин. Функция main , сама по себе, является горутиной. Синтаксис очень прост для создания горутины.

Пример горутины: go sum(arr []int)
В этом примере мы увидим, как горутины работают с каналами без использования групп ожидания. Теперь давайте посмотрим, что такое канал!

Что такое каналы?
Можно считать каналы конвейерами, через которые вы можете подключать одновременно работающие горутины. Вы можете отправлять данные (любого типа) в каналы из одной горутины, например done <- true, и читать те же данные в другой горутине, например status <- done.

Давайте узнаем о типах каналов, которые у нас есть. В Golang есть два типа каналов: буферизованные и небуферизованные.

Буферизованные каналы

Буферизованный канал имеет очередь элементов. Максимальный размер очереди определяется при создании канала с помощью аргумента емкости функции make. Создаем канал емкостью 3

ch = make(chan string, 3)

Операция отправления в буферизованный канал вставляет отправляемый элемент в конец очереди, а операция получения удаляет первый элемент из очереди. Если канал заполнен, операция отправления блокирует свою go-подпрограмму до тех пор, пока другая go-подпрограмма не освободит место, получив данные из канала. И наоборот, если канал пуст, операция получения блокирует горутину до того момента, пока в канал не будет послано значение из другой горутинуы.
Пример: сделано := make(chan bool, 1)
В приведенном выше примере показано, что канал, ожидающий логических значений, может иметь только одно значение за раз. Второе значение сможет войти в канал только после того, как из него будет взято первое. Если мы попытаемся передать другое значение, не извлекая первое, это приведет к взаимоблокировке, а мы этого не хотим.
Небуферизованные каналы: с этими каналами не связан максимальный размер, но нам нужен

Небуферизованные каналы

Операция отправления в небуферизованный канал блокирует go-подпрограмму до тех пор, пока другая go-подпрограмма не выполнит соответствующее получение из того же канала, после чего значение становится переданным, и обе go-подпрограммы продолжаются. И наоборот, если первой сделана попытка выполнить операцию получения, принимающая go-подпрограмма блокируется до тех пор, пока другая go- подпрограмма не выполнит отправление значения в тот же канал. Связь по небуферизованному каналу приводит к синхронизации операций отправления и получения. По этой причине небуферизованные каналы иногда называют синхронными. Когда значение отправляется в небуферизованный канал, получение значения предшествует продолжению работы отправляющей горутины.
Пример: sum := make(chan int)
Приведенный выше пример показывает, что sum канала ожидает, что в нее будут помещены целые значения.
Это было краткое введение в горутины и каналы.
Постановка задачи: вам дан массив целых чисел, и вы должны просуммировать элементы массива в одной программе и вывести сумму один за другим в отдельной программе.
Вот решение проблемы:





package main
import "fmt"func main() {
 sum := make(chan int)
 done := make(chan bool, 1)
 arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
 go add(arr, sum, done)
 go print(sum, done)
 <-done
}func add(arr []int, sum chan int, done chan bool) {
  adds := 0
  for _, i := range arr {
    adds += i
    sum <- adds
  }
  done <- true
}func print(sum chan int, done chan bool) {
  for {
    select {
      case x := <- sum:
        fmt.Println(x)
      case <- done:
        done <- true
        break
    }
  }
}

Как видите, я использовал функцию add в качестве горутины для сложения элементов массива и функцию печати для вывода суммы. sum — это небуферизованный канал, который заботится о передаче суммы элементов массива из процедуры добавления в функцию print. И я использовал другой канал (буферизованный) с именем done. Вам, наверное, интересно, почему! Это связано с тем, что, как упоминалось ранее, горутины в go не зависят от блока кода, из которого они вызываются, поэтому, если я не использую канал done, ваша программа ничего не напечатает (вы можете попробовать эту программу).
Канал done используется здесь, чтобы дождаться завершения выполнения горутину, чтобы мы могли увидеть желаемый результат!
Вот и все по этой теме! Надеюсь, это полезная информация для вас.

Ответить