Sobes.tech
Middle

Какво е мютекс, какви типове мютекси съществуват и как да ги използваме?

sobes.tech AI

Отговор от AI

Мютексът (от английски MUTual EXclusion) е примитив за синхронизация, използван за осигуряване на изключителен достъп до споделен ресурс между конкуриращи се горутини. Той гарантира, че във всеки момент само една горутина може да притежава мютекса и да достъпва защитените данни.

В Go пакетът sync предоставя два основни типа мютекси:

  • sync.Mutex: Стандартен мютекс. Предоставя методите Lock() и Unlock(). Ако мютексът вече е блокиран, извикването на Lock() блокира текущата горутина, докато не бъде освободен. Unlock() освобождава мютекса.

    var mu sync.Mutex
    var counter int
    
    func increment() {
        mu.Lock() // Блокира достъпа до counter
        counter++
        mu.Unlock() // Освобождава достъпа
    }
    
  • sync.RWMutex: Мютекс за четене/писане. Позволява на множество горутини да четат едновременно защитения ресурс, но само една да пише. Предоставя методите Lock(), Unlock() (за писане), RLock() и RUnlock() (за четене).

    var rwMu sync.RWMutex
    var data []int
    
    func readData() {
        rwMu.RLock() // Блокира за четене
        // Четене на data
        fmt.Println(data)
        rwMu.RUnlock() // Освобождава за четене
    }
    
    func writeData(val int) {
        rwMu.Lock() // Блокира за писане
        // Запис в data
        data = append(data, val)
        rwMu.Unlock() // Освобождава за писане
    }
    

Използването на мютекси е необходимо за предотвратяване на състезателни условия (race conditions), когато няколко горутини едновременно се опитват да модифицират едни и същи данни, водещи до непредсказуеми резултати.

Правила за използване:

  1. Извикайте Lock() преди достъп до защитения ресурс.
  2. Извикайте Unlock() след приключване на достъпа. Препоръчително е да използвате defer mu.Unlock() за гарантиране на освобождаването дори при паника.
  3. Не се опитвайте да блокирате вече блокиран мютекс от същата горутина (може да доведе до deadlock).
  4. Не се опитвайте да отключите незаключен мютекс.
  5. При използване на RWMutex, използвайте RLock/RUnlock за операции за четене и Lock/Unlock за операции за писане.
  6. Мютексите не трябва да се копират след първото им използване. Предавайте ги по референция или указател.

Пример за използване на defer:

func safeIncrement() {
    mu.Lock()
    defer mu.Unlock() // Гарантира освобождаване
    counter++
}