← Назад ко всем вопросам

В каких режимах могут работать Mutex

1️⃣ Как кратко ответить

Mutex в Go может работать в двух режимах: обычный (normal) и RWMutex (read-write). Обычный Mutex позволяет только одному потоку захватить блокировку, в то время как RWMutex позволяет нескольким потокам одновременно читать данные, но только одному потоку записывать.

2️⃣ Подробное объяснение темы

Mutex (сокращение от "mutual exclusion") — это механизм синхронизации, который используется для предотвращения одновременного доступа к общим ресурсам несколькими потоками. В Go стандартная библиотека предоставляет два типа Mutex: sync.Mutex и sync.RWMutex.

Обычный Mutex

sync.Mutex — это простой механизм блокировки, который позволяет только одному потоку захватить блокировку в любой момент времени. Это означает, что если один поток захватил блокировку, все остальные потоки, которые пытаются захватить ту же блокировку, будут заблокированы до тех пор, пока первый поток не освободит её.

Пример использования sync.Mutex:

package main
​
import (
	"fmt"
	"sync"
)
​
var (
	counter int
	mutex   sync.Mutex
)
​
func increment(wg *sync.WaitGroup) {
	defer wg.Done()
	mutex.Lock()   // Захватываем блокировку
	counter++      // Увеличиваем общий счетчик
	mutex.Unlock() // Освобождаем блокировку
}
​
func main() {
	var wg sync.WaitGroup
​
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go increment(&wg)
	}
​
	wg.Wait()
	fmt.Println("Final Counter:", counter)
}
  • mutex.Lock(): Захватывает блокировку, чтобы другие потоки не могли изменить counter.
  • counter++: Увеличивает значение общего счетчика.
  • mutex.Unlock(): Освобождает блокировку, позволяя другим потокам продолжить выполнение.

RWMutex

sync.RWMutex — это более сложный механизм блокировки, который позволяет нескольким потокам одновременно читать данные, но только одному потоку записывать. Это полезно, когда у вас есть много операций чтения и относительно мало операций записи.

Пример использования sync.RWMutex:

package main
​
import (
	"fmt"
	"sync"
)
​
var (
	counter int
	rwMutex sync.RWMutex
)
​
func readCounter(wg *sync.WaitGroup) {
	defer wg.Done()
	rwMutex.RLock() // Захватываем блокировку для чтения
	fmt.Println("Counter:", counter)
	rwMutex.RUnlock() // Освобождаем блокировку для чтения
}
​
func writeCounter(wg *sync.WaitGroup) {
	defer wg.Done()
	rwMutex.Lock()   // Захватываем блокировку для записи
	counter++        // Изменяем общий счетчик
	rwMutex.Unlock() // Освобождаем блокировку для записи
}
​
func main() {
	var wg sync.WaitGroup
​
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go writeCounter(&wg)
	}
​
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go readCounter(&wg)
	}
​
	wg.Wait()
}
  • rwMutex.RLock(): Захватывает блокировку для чтения, позволяя другим потокам также читать.
  • rwMutex.RUnlock(): Освобождает блокировку для чтения.
  • rwMutex.Lock(): Захватывает блокировку для записи, блокируя все другие операции чтения и записи.
  • rwMutex.Unlock(): Освобождает блокировку для записи.

Зачем это нужно

Mutex необходимы для обеспечения корректности программы при работе с общими ресурсами в многопоточной среде. Без использования Mutex, несколько потоков могут одновременно изменять данные, что может привести к некорректным результатам и состояниям гонки (race conditions). Использование sync.Mutex и sync.RWMutex позволяет избежать таких проблем, обеспечивая безопасный доступ к общим данным.

Тема: Конкурентность
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки