Что такое Go
Go (Golang) — язык программирования, созданный в Google. Он спроектирован для простоты, производительности и масштабируемости. Ключевая особенность Go — встроенная поддержка конкурентности через goroutines и channels.
Goroutines
▸Что такое goroutine
Goroutine — это лёгкий поток, управляемый runtime Go. Goroutines потребляют всего 2 КБ памяти и могут запускаться тысячами.
▸Запуск goroutine
1package main23import (4 "fmt"5 "time"6)78func worker(id int) {9 fmt.Printf("Worker %d starting\n", id)10 time.Sleep(time.Second)11 fmt.Printf("Worker %d done\n", id)12}1314func main() {15 // Запуск goroutine16 go worker(1)17 go worker(2)1819 // Ожидание завершения20 time.Sleep(2 * time.Second)21 fmt.Println("Main done")22}
▸WaitGroup
1package main23import (4 "fmt"5 "sync"6)78func main() {9 var wg sync.WaitGroup1011 for i := 1; i <= 5; i++ {12 wg.Add(1)13 go func(id int) {14 defer wg.Done()15 fmt.Printf("Worker %d\n", id)16 }(i)17 }1819 wg.Wait()20 fmt.Println("All workers done")21}
Channels
▸Что такое channel
Channel — это средство связи между goroutines. Каналы позволяют безопасно передавать данные между конкурентными потоками.
▸Простой канал
1package main23import "fmt"45func main() {6 // Создание канала7 ch := make(chan string)89 // Отправка данных (в goroutine)10 go func() {11 ch <- "Hello from goroutine"12 }()1314 // Получение данных15 msg := <-ch16 fmt.Println(msg)17}
▸Буферизированные каналы
1func main() {2 // Буфер на 3 элемента3 ch := make(chan int, 3)45 ch <- 16 ch <- 27 ch <- 389 fmt.Println(<-ch) // 110 fmt.Println(<-ch) // 211 fmt.Println(<-ch) // 312}
▸Закрытие каналов
1func producer(ch chan<- int) {2 for i := 0; i < 10; i++ {3 ch <- i4 }5 close(ch) // Закрытие канала6}78func consumer(ch <-chan int) {9 for value := range ch { // Автоматическая проверка закрытия10 fmt.Println(value)11 }12}1314func main() {15 ch := make(chan int)16 go producer(ch)17 consumer(ch)18}
Select
▸Мультиплексирование каналов
1func main() {2 ch1 := make(chan string)3 ch2 := make(chan string)45 go func() {6 time.Sleep(1 * time.Second)7 ch1 <- "one"8 }()910 go func() {11 time.Sleep(2 * time.Second)12 ch2 <- "two"13 }()1415 for i := 0; i < 2; i++ {16 select {17 case msg := <-ch1:18 fmt.Println("Received from ch1:", msg)19 case msg := <-ch2:20 fmt.Println("Received from ch2:", msg)21 }22 }23}
▸Таймаут через select
1func main() {2 ch := make(chan string)34 go func() {5 time.Sleep(3 * time.Second)6 ch <- "result"7 }()89 select {10 case result := <-ch:11 fmt.Println("Got result:", result)12 case <-time.After(2 * time.Second):13 fmt.Println("Timeout!")14 }15}
Паттерны конкурентности
▸Fan-out, Fan-in
1func fanOut(input <-chan int, workers int) []<-chan int {2 channels := make([]<-chan int, workers)3 for i := 0; i < workers; i++ {4 channels[i] = process(input)5 }6 return channels7}89func fanIn(channels ...<-chan int) <-chan int {10 var wg sync.WaitGroup11 merged := make(chan int)1213 for _, ch := range channels {14 wg.Add(1)15 go func(c <-chan int) {16 defer wg.Done()17 for value := range c {18 merged <- value19 }20 }(ch)21 }2223 go func() {24 wg.Wait()25 close(merged)26 }()2728 return merged29}
▸Pipeline
1func generate(nums ...int) <-chan int {2 out := make(chan int)3 go func() {4 for _, n := range nums {5 out <- n6 }7 close(out)8 }()9 return out10}1112func square(in <-chan int) <-chan int {13 out := make(chan int)14 go func() {15 for n := range in {16 out <- n * n17 }18 close(out)19 }()20 return out21}2223func main() {24 // Pipeline: generate -> square25 for value := range square(generate(2, 3, 4)) {26 fmt.Println(value) // 4, 9, 1627 }28}
▸Context для отмены
1func worker(ctx context.Context) {2 for {3 select {4 case <-ctx.Done():5 fmt.Println("Worker cancelled:", ctx.Err())6 return7 default:8 fmt.Println("Working...")9 time.Sleep(100 * time.Millisecond)10 }11 }12}1314func main() {15 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)16 defer cancel()1718 go worker(ctx)19 time.Sleep(time.Second)20}
Mutex
▸Синхронизация доступа
1type Counter struct {2 mu sync.Mutex3 value int4}56func (c *Counter) Increment() {7 c.mu.Lock()8 defer c.mu.Unlock()9 c.value++10}1112func (c *Counter) Value() int {13 c.mu.Lock()14 defer c.mu.Unlock()15 return c.value16}
Производительность
▸Бенчмарки
1func BenchmarkWorker(b *testing.B) {2 for i := 0; i < b.N; i++ {3 // Тестируемая функция4 }5}
▸Профилирование
1import "runtime/pprof"23func main() {4 f, _ := os.Create("cpu.prof")5 pprof.StartCPUProfile(f)6 defer pprof.StopCPUProfile()7 // Код для профилирования8}
Заключение
goroutines и channels — ключевые особенности Go для конкурентного программирования. Понимание паттернов (fan-out, pipeline, context) критически важно для написания высокопроизводительных бэкенд-приложений. На собеседовании спрашивают про разницу между goroutines и потоками, как работает scheduler, и типичные паттерны.