Тип sync.RWMutex
Тип sync.RWMutex — это мьютекс с возможностью разделяемой блокировки
для чтения. Он позволяет множественным горутинам одновременно
получать доступ на чтение, но блокирует доступ на запись во время
операций чтения. Такой подход значительно повышает производительность
при частом чтении данных и редкой записи.
У типа sync.RWMutex есть два основных метода для блокировки:
RLock для чтения и Lock для записи. Для снятия
блокировок используются методы RUnlock и Unlock
соответственно.
Синтаксис
var mu sync.RWMutex
// Для чтения
mu.RLock()
// чтение данных
mu.RUnlock()
// Для записи
mu.Lock()
// запись данных
mu.Unlock()
Пример использования RWMutex для чтения
Давайте создадим структуру счетчика с разделяемым доступом и организуем одновременное чтение данных:
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.RWMutex
value int
}
func (c *Counter) Read() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.value
}
func main() {
counter := &Counter{value: 42}
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
res := counter.Read()
fmt.Println(res)
}()
}
wg.Wait()
}
Результат выполнения кода:
"42"
"42"
"42"
"42"
"42"
Пример использования RWMutex для записи
Теперь добавим метод записи и проверим, как блокировка на запись влияет на чтение:
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.RWMutex
value int
}
func (c *Counter) Write(val int) {
c.mu.Lock()
defer c.mu.Unlock()
c.value = val
fmt.Println("Written:", val)
}
func (c *Counter) Read() int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.value
}
func main() {
counter := &Counter{value: 10}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
counter.Write(99)
}()
wg.Add(1)
go func() {
defer wg.Done()
res := counter.Read()
fmt.Println("Read:", res)
}()
wg.Wait()
}
Результат выполнения кода (порядок может отличаться):
"Written: 99"
"Read: 99"
Пример с несколькими читателями и одним писателем
Покажем, как RWMutex позволяет одновременно читать
данные нескольким горутинам, но блокирует запись:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var mu sync.RWMutex
data := "initial"
// Читатели
for i := 0; i < 3; i++ {
go func(id int) {
mu.RLock()
fmt.Printf("Reader %d: %s\n", id, data)
time.Sleep(100 * time.Millisecond)
mu.RUnlock()
}(i)
}
// Писатель
time.Sleep(50 * time.Millisecond)
go func() {
mu.Lock()
data = "updated"
time.Sleep(200 * time.Millisecond)
mu.Unlock()
}()
time.Sleep(500 * time.Millisecond)
}
Результат выполнения кода (порядок строк может отличаться):
"Reader 2: initial"
"Reader 0: initial"
"Reader 1: initial"
Смотрите также
-
тип
sync.Mutex,
который предоставляет обычную блокировку без разделения на чтение/запись -
метод
RLock,
который блокирует мьютекс для чтения -
метод
Lock,
который блокирует мьютекс для записи -
пакет
atomic,
который предоставляет атомарные операции для конкурентного доступа без блокировок