Мьютексы

Board index Программирование GoLand

Description: Программирование на языке Go

#1by mexan » 02.01.2025, 22:37

Мьютекс (или взаимный исключатель) — это синхронизационный примитив, который используется для защиты общих ресурсов от одновременного доступа нескольких горутин. Он предотвращает состояние гонки (race condition), которое может возникнуть, если две или более горутины пытаются одновременно изменять одно и то же значение или ресурс.

Тип sync.Mutex — примитив, реализующий мьютекс в Go. Для переменной этого типа можно вызвать два метода:
(m *Mutex) Lock() - блокирует мьютекс. Заблокировать мьютекс может только одна горутина. Если другая горутина вызывает этот метод, а мьютекс уже заблокирован, то она будет ждать его освобождения.
(m *Mutex) Unlock() - разблокирует мьютекс. Горутина должна освободить мьютекс сразу после того, как она закончила выполнение критической секции.

Как работает мьютекс?
  1. Блокировка (Lock): Когда горутина хочет получить доступ к защищенному ресурсу, она делает запрос на блокировку мьютекса. Если мьютекс свободен (не заблокирован), горутина получает доступ к ресурсу и мьютекс блокируется. Если мьютекс уже занят другой горутиной, то текущая горутина будет ждать, пока мьютекс не освободится.
  2. Разблокировка (Unlock): После того как горутина завершит работу с защищенным ресурсом, она должна разблокировать мьютекс. Это позволит другим горутинам получить доступ к этому ресурсу.

Важно помнить:
  • Не забывайте разблокировать мьютекс: Если вы блокируете мьютекс и не разблокируете его, это приведет к взаимной блокировке (deadlock), где горутины будут ожидать друг друга бесконечно.
  • Используйте defer: Часто рекомендуется использовать defer для разблокировки мьютекса. Это гарантирует, что мьютекс будет разблокирован, даже если код внутри блока (например, обработка ошибок) завершится преждевременно.
  • Мьютексы не предназначены для управления порядком выполнения горутин: Они лишь обеспечивают безопасность доступа к ресурсам, но не предоставляют механизм для "особого запуска" горутин. Чтобы обеспечить порядок выполнения, используются другие механизмы синхронизации, такие как каналы.

Таким образом, мьютексы полезны, когда вам нужно гарантировать, что только одна горутина работает с определенным ресурсом в любой момент времени, но они не контролируют сам порядок вызова горутин.
mexan
Администратор
Reputation: 0
Posts: 179
Topics: 138

#2by mexan » 02.01.2025, 22:39

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

Основные функции мьютекса
  • Защита общих ресурсов: Мьютексы предназначены для предотвращения одновременного доступа к общим ресурсам (например, переменным или структурам данных) несколькими горутинами. Это защищает данные от повреждений в результате состояния гонки (race condition).
  • Синхронизация доступа: Когда одна горутина захватывает мьютекс, другие горутины, которые пытаются захватить тот же мьютекс, будут блокироваться до тех пор, пока мьютекс не будет освобожден. Это позволяет последовательно обрабатывать операции, которые требуют изменения общего состояния.

Что означает "привязка" горутины к процессу?
  • Планировщик: Горутины управляются планировщиком Go, который сам по себе решает, как и где выполняются горутины. Мьютекс не управляет горутинами или их поведением на уровне процесса (или потоков). Вместо этого он обеспечивает безопасность общих ресурсов.
  • Нет "привязки": Используя мьютекс, вы не присваиваете конкретной горутине право на выполнение работы или не ограничиваете её только одним ресурсом. Вместо этого вы гарантируете, что только одна горутина может получить доступ к ресурсу в данный момент времени.

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

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

Пример:
Code: Select all
package main

import (
   "fmt"
   "sync"
)

var list []int
var wg sync.WaitGroup
var mu sync.Mutex

func do() {
   defer wg.Done()
   for i := 0; i < 100; i++ {
      mu.Lock()
      list = append(list, i)
      mu.Unlock()
   }
}

func main() {
   for i := 0; i < 8; i++ {
      wg.Add(1)
      go do()
   }
   wg.Wait()
   // проверка содержимого у слайса
   sum := 0
   for _, v := range list {
      sum += v
   }
   fmt.Println(len(list), sum)
}

  • wg.Add(1): Мы добавляем только 1 для каждой горутины в цикле. Это правильно, так как каждая горутина завершает работу, вызывая wg.Done().
  • defer wg.Done(): Мы используем defer в начале функции do(). Это гарантирует, что wg.Done() будет вызван даже если функция завершит работу из-за ошибки.
  • wg.Wait(): Этот вызов нужно разместить после запуска всех горутин, чтобы программа ожидала их завершения перед тем, как продолжить выполнение (например, вычисление суммы).
mexan
Администратор
Reputation: 0
Posts: 179
Topics: 138


Return to GoLand

cron