img

Основы goroutine, mutex и waitgroup в Go

Этот пост посвящен goroutine, mutex и waitgroup. Если вы не смотрели базовую часть - часть 1, рекомендуем сначала ознакомиться с ней.

icon strelka icons icons

узнай больше на курсе

Python программист с нуля
Стань разработчиком на одном из самых популярных языков программирования - Python
Подробнее о курсе
Java-разработчик с нуля
Освойте backend-разработку и программирование на Java, фреймворки Spring и Maven, работу с базами данных и API
Подробнее о курсе
C# разработчик с нуля
На курсе ты освоишь основы программирования на C#, включая синтаксис, объектно-ориентированное программирование и асинхронное программирование.
Подробнее о курсе

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

Подход "обучение через практику" работает лучше всего!

package main

import (
 "fmt"
 "sort"
 "strings"
 "sync"
 "unicode"
)

func main() {

 // Below array of 5 strings are simulating 5 files. 
 // Now I want to read all 5 files in asynchronously in parallel manner with the help of
 // GO Routine 
 // Task 1 - collect all words in a file and its frequency. 
 // Task 2 - Combine all results and then give top frequently occuring words. 

 files := []string{
  "Go is a powerful language for concurrent programming tasks today.",
  "Hello world in Go makes coding fun and efficient always.",
  "Concurrent code with goroutines is what makes Go shine bright.",
  "Programming in Go handles errors well and keeps things simple.",
  "World of Go developers love its speed and reliability features.",
 }

 // Global frequency map and mutex for thread safety
 // Variable declared in main or at file level are global variables in a file. 

 var freqMap = make(map[string]int) // Key-String , Count - int
 var mu sync.Mutex
 var wg sync.WaitGroup

 fmt.Println("Main: Starting to process files...")

 // Process every "file / String " concurrently - outer for loop 
 for i, content := range files {

  wg.Add(1) // Count asychronous tasks . Add(1) for every Go routine 

  fmt.Printf("Main: Adding to WaitGroup and launching goroutine for file %d\n", i)

  // nameless function for GO routine 
  go func(idx int, text string) {

   // Watch the logs in console to understand the thread like behaviour of GO Routines 
   fmt.Printf("Goroutine %d: Started processing\n", idx)

   defer wg.Done() // Similar to final block in Java called when function exists 

   // Local frequency map for every file created in every iteration 
   localFreq := make(map[string]int)

   // Clean and split into words into "text" containing file text
   words := strings.Fields(text)

   fmt.Printf("Goroutine %d: Split text into %d words\n", idx, len(words))

   // Inner for loop - processing individual words collected in a "words" array
   for _, word := range words {

    // Remove punctuation and convert to lowercase
    // rune is for single charater 
    cleanWord := strings.TrimFunc(strings.ToLower(word), func(r rune) bool {
     return !unicode.IsLetter(r)
    })

    if cleanWord != "" {
     localFreq[cleanWord]++ // Add word in a local map and increment count
    }
   }

   fmt.Printf("Goroutine %d: Finished counting local frequencies (map size: %d)\n", idx, len(localFreq))

   // Merge into global map safely
   fmt.Printf("Goroutine %d: Attempting to lock mutex for merging\n", idx)

   mu.Lock() // aquire lock to modify global map

   fmt.Printf("Goroutine %d: Acquired mutex lock\n", idx)

   for word, count := range localFreq {
    freqMap[word] += count
   }

   mu.Unlock()

   fmt.Printf("Goroutine %d: Released mutex lock after merging\n", idx)

   // Log completion
   fmt.Printf("Goroutine %d: Finished processing and calling wg.Done()\n", idx)

  }(i, content) // Nameless GO function ends here & called with index and string item

 }

 // Wait for all goroutines to finish
 fmt.Println("Main: All goroutines launched; now waiting on WaitGroup...")

 wg.Wait() // Control stops here and wait for all Goroutines to finish 

 fmt.Println("Main: WaitGroup complete; all goroutines finished. Proceeding to sort and print.")

 // If no words found, handle gracefully
 if len(freqMap) == 0 {
  fmt.Println("No words found in the provided contents.")
  return
 }

 // Prepare a slice for sorting
 type wordFreq struct {
  Word  string
  Count int
 }

 var freqList []wordFreq

 // Similar to Javascript declare variables any where in code :( Good or Bad ? 
 for word, count := range freqMap { // Pay attention count is not index!!!
  freqList = append(freqList, wordFreq{word, count})
 }

 fmt.Printf("Main: Prepared frequency list for sorting (size: %d)\n", len(freqList))

 // Sort by count descending
 sort.Slice(freqList, func(i, j int) bool {
  return freqList[i].Count > freqList[j].Count
 })

 fmt.Println("Main: Sorted the frequency list.")

 // Print top 5 (or fewer if less than 5)
 topN := 5
 if len(freqList) < topN {
  topN = len(freqList)
 }

 fmt.Println("Top 5 most frequent words:")

 for i := 0; i < topN; i++ {
  fmt.Printf("%s: %d\n", freqList[i].Word, freqList[i].Count)
 }

 fmt.Println("Main: Program complete.")
}

Ключевое слово GO запускает функцию асинхронно. В фоне создается goroutine для выполнения этой функции.

Если нужно, чтобы основной поток дождался завершения всех goroutine, используется WaitGroup.

waitgroup wg.add(N) показывает, что запущено N goroutine. Каждый defer wg.done() уменьшает счетчик на 1. Когда счетчик достигает нуля, ожидание завершается.

Mutex.Lock и Mutex.Unlock работают очевидным образом - они предотвращают одновременное выполнение участка кода несколькими потоками.

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

Хотите запустить и протестировать код - откройте его в Go Playground. Перед этим не забудьте поставить пару лайков :)

Ссылка
скопирована
Получите бесплатные уроки на наших курсах
Все курсы
icon strelka icons icons

узнай больше на курсе

Python программист с нуля
Стань разработчиком на одном из самых популярных языков программирования - Python
Подробнее о курсе
Java-разработчик с нуля
Освойте backend-разработку и программирование на Java, фреймворки Spring и Maven, работу с базами данных и API
Подробнее о курсе
C# разработчик с нуля
На курсе ты освоишь основы программирования на C#, включая синтаксис, объектно-ориентированное программирование и асинхронное программирование.
Подробнее о курсе
Фронтенд-разработчик с нуля
Погрузитесь в мир веб-разработки, освоив основные инструменты работы: HTML, CSS, JavaScript
Подробнее о курсе
Разработка приложений на Flutter и Dart
Научись создавать кроссплатформенные приложения на Flutter, освой язык Dart
Подробнее о курсе
Автоматизированное тестирование на Python
Изучите автоматизацию тестирования на Python чтобы стать востребованным специалистом
Подробнее о курсе
Еще по теме:
img
Разбираем, какую AI-модель выбрать под задачу: ChatGPT, Claude, Gemini или NotebookLM - их сильные стороны и ограничения
img
Почему диалог с ИИ снижает продуктивность и как перейти к декларативному управлению задачами вместо бесконечных чат-сессий
img
Простое объяснение 20 базовых концепций искусственного интеллекта: от нейросетей до агентов и RAG без сложных терминов.
img
Разбираем причины выгорания senior Android разработчиков: скрытая нагрузка, архитектурные решения и рост требований в экосистеме
img
Обзор языка Go: ключевые слова, структуры, каналы, горутины и особенности синтаксиса для разработки backend и микросервисов.
ВЕСЕННИЕ СКИДКИ
30%
40%
50%
До конца акции: 30 дней 24 : 59 : 59