@porter.kertzmann
Go был разработан с учетом многопоточности, и в языке есть много встроенных средств для создания и управления горутинами (goroutine), которые позволяют выполнять задачи параллельно.
В Go каждая горутина представляет собой легковесный поток выполнения, которым управляет планировщик горутин (goroutine scheduler). Каждая горутина запускается с помощью ключевого слова go
, и ее выполнение происходит параллельно с выполнением других горутин.
Пример использования многопоточности в Go:
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 |
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(2) // первая горутина go func() { defer wg.Done() fmt.Println("Первая горутина") }() // вторая горутина go func() { defer wg.Done() fmt.Println("Вторая горутина") }() wg.Wait() fmt.Println("Все горутины завершились") } |
В этом примере создаются две горутины, каждая из которых выводит сообщение в консоль. Функция sync.WaitGroup
используется для ожидания завершения обеих горутин.
Также в Go есть механизмы для обеспечения безопасности при работе с общими ресурсами в многопоточной среде, такие как мьютексы (mutex) и каналы (channel). Мьютексы позволяют блокировать доступ к общим ресурсам, пока они не освободятся, а каналы предоставляют способ взаимодействия между горутинами.
@porter.kertzmann
Например, вот как можно использовать мьютексы и каналы в Go:
Мьютексы:
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 36 37 38 39 40 41 42 43 |
package main import ( "fmt" "sync" ) var counter int var mutex sync.Mutex func increment() { mutex.Lock() defer mutex.Unlock() counter++ } func decrement() { mutex.Lock() defer mutex.Unlock() counter-- } func main() { var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for i := 0; i < 1000; i++ { increment() } }() go func() { defer wg.Done() for i := 0; i < 1000; i++ { decrement() } }() wg.Wait() fmt.Println("Counter:", counter) } |
В этом примере создаются две горутины, одна увеличивает счетчик, а другая уменьшает его. Они оба используют мьютекс, чтобы блокировать доступ к общей переменной counter и гарантировать, что значение не будет модифицировано параллельно. В результате получаем значение счетчика равное 0.
Каналы:
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 |
package main import "fmt" func sum(numbers []int, c chan<- int) { sum := 0 for _, num := range numbers { sum += num } c <- sum } func main() { numbers := []int{1, 2, 3, 4, 5} // создаем канал c := make(chan int) // запускаем горутину для вычисления суммы и передачи результата в канал go sum(numbers, c) // получаем результат из канала result := <-c fmt.Println("Sum:", result) } |
В этом примере создается канал, через который горутина sum передает результат вычисления суммы чисел в главную горутину. С помощью оператора <- c главная горутина получает результат из канала и выводит его в консоль.
Это лишь некоторые примеры использования многопоточности в Go. Язык предлагает множество других возможностей и инструментов для работы с горутинами, и вы можете изучить их в документации по Go.