Пакет sync
Обзор
Пакет sync предоставляет базовые примитивы синхронизации, такие как блокировки взаимного исключения. За исключением типов Once и WaitGroup, большинство из них предназначено для использования низкоуровневыми библиотечными функциями. Для синхронизации на более высоком уровне предпочтительнее использовать каналы и обмен сообщениями.
Значения, содержащие типы, определённые в этом пакете, не следует копировать.
Указатель
Файлы пакета
cond.go map.go mutex.go once.go oncefunc.go pool.go poolqueue.go runtime.go runtime2.go rwmutex.go waitgroup.go
func OnceFunc 1.21
func OnceFunc(f func()) func()
OnceFunc возвращает функцию, которая вызывает f только один раз. Возвращённую функцию можно вызывать одновременно из нескольких горутин.
Если f вызывает панику, возвращённая функция будет при каждом вызове вызывать панику с тем же значением.
func OnceValue 1.21
func OnceValue[T any](f func() T) func() T
OnceValue возвращает функцию, которая вызывает f только один раз и возвращает значение, возвращённое f. Возвращённую функцию можно вызывать одновременно из нескольких горутин.
Если f вызывает панику, возвращённая функция будет при каждом вызове вызывать панику с тем же значением.
Пример
В этом примере OnceValue используется для выполнения «дорогостоящего» вычисления только один раз, даже при одновременном вызове из нескольких горутин.
Код:
once := sync.OnceValue(func() int {
sum := 0
for i := range 1000 {
sum += i
}
fmt.Println("Computed once:", sum)
return sum
})
done := make(chan bool)
for range 10 {
go func() {
const want = 499500
got := once()
if got != want {
fmt.Println("want", want, "got", got)
}
done <- true
}()
}
for range 10 {
<-done
}
Вывод:
Computed once: 499500
func OnceValues 1.21
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)
OnceValues возвращает функцию, которая вызывает f только один раз и возвращает значения, возвращённые f. Возвращённую функцию можно вызывать одновременно из нескольких горутин.
Если f вызывает панику, возвращённая функция будет при каждом вызове вызывать панику с тем же значением.
Пример
В этом примере OnceValues используется для чтения файла только один раз.
Код:
once := sync.OnceValues(func() ([]byte, error) {
fmt.Println("Reading file once")
return os.ReadFile("example_test.go")
})
done := make(chan bool)
for range 10 {
go func() {
data, err := once()
if err != nil {
fmt.Println("error:", err)
}
_ = data // Ignore the data for this example
done <- true
}()
}
for range 10 {
<-done
}
Вывод:
Reading file once
type Cond
Cond реализует условную переменную — точку встречи для горутин, ожидающих наступления события или сообщающих о нём.
Каждый Cond связан с блокировкой Locker L (часто это *Mutex или *RWMutex), которая должна быть захвачена при изменении условия и при вызове метода Cond.Wait.
Cond нельзя копировать после первого использования.
В терминологии модели памяти Go Cond обеспечивает, что вызов Cond.Broadcast или Cond.Signal «синхронизируется до» любого вызова Wait, который он разблокирует.
Во многих простых случаях вместо Cond удобнее использовать каналы (Broadcast соответствует закрытию канала, а Signal — отправке значения в канал).
Подробнее о замене sync.Cond см. в серии Roberto Clapis о продвинутых шаблонах конкурентного программирования, а также в докладе Bryan Mills о шаблонах конкурентного программирования.
type Cond struct {
// L is held while observing or changing the condition
L Locker
// contains filtered or unexported fields
}
func NewCond
func NewCond(l Locker) *Cond
NewCond возвращает новый Cond с блокировкой Locker l.
func (*Cond) Broadcast
func (c *Cond) Broadcast()
Broadcast пробуждает все горутины, ожидающие на c.
Вызывающий код может удерживать c.L во время вызова, но это необязательно. Время выполнения Broadcast пропорционально числу ожидающих горутин; учитывайте, что удержание блокировки во время вызова Broadcast увеличит время, в течение которого блокировка будет занята.
func (*Cond) Signal
func (c *Cond) Signal()
Signal пробуждает одну горутину, ожидающую на c, если такая есть.
Вызывающий код может удерживать c.L во время вызова, но это необязательно.
Signal() не влияет на приоритет планирования горутин; если другие горутины пытаются захватить c.L, они могут быть разбужены раньше «ожидающей» горутины.
func (*Cond) Wait
func (c *Cond) Wait()
Wait атомарно освобождает c.L и приостанавливает выполнение вызывающей горутины. После возобновления выполнения Wait захватывает c.L, прежде чем вернуться. В отличие от других систем, Wait не может вернуться, если его не разбудил Cond.Broadcast или Cond.Signal.
Поскольку во время ожидания Wait блокировка c.L не удерживается, вызывающий код обычно не может предполагать, что при возврате Wait условие истинно. Вместо этого следует вызывать Wait в цикле:
c.L.Lock()
for !condition() {
c.Wait()
}
... make use of condition ...
c.L.Unlock()
type Locker
Locker представляет объект, который можно заблокировать и разблокировать.
type Locker interface {
Lock()
Unlock()
} type Map 1.9
Map похож на Go map[any]any, но безопасен для одновременного использования несколькими горутинами без дополнительной блокировки или координации. Операции чтения, записи и удаления выполняются за амортизированное постоянное время.
Тип Map специализирован. Для большей типобезопасности и упрощения поддержки других инвариантов наряду с содержимым map в большинстве случаев следует использовать обычную Go map с отдельной блокировкой или координацией.
Тип Map оптимизирован для двух распространённых случаев: (1) запись элемента для заданного ключа выполняется только один раз, а чтение — много раз, как в постоянно растущих кэшах; (2) несколько горутин читают, записывают и перезаписывают элементы для непересекающихся наборов ключей. В этих случаях использование Map может значительно снизить конкуренцию за блокировку по сравнению с Go map в сочетании с отдельной Mutex или RWMutex.
Нулевое значение Map — пустая карта, готовая к использованию. Map нельзя копировать после первого использования.
В терминологии модели памяти Go Map обеспечивает, что операция записи «синхронизируется до» любой операции чтения, которая наблюдает эффект этой записи. Операции чтения и записи определяются следующим образом. Map.Load, Map.LoadAndDelete, Map.LoadOrStore, Map.Swap, Map.CompareAndSwap и Map.CompareAndDelete являются операциями чтения; Map.Delete, Map.LoadAndDelete, Map.Store и Map.Swap являются операциями записи; Map.LoadOrStore является операцией записи, если возвращает loaded со значением false; Map.CompareAndSwap является операцией записи, если возвращает swapped со значением true; Map.CompareAndDelete является операцией записи, если возвращает deleted со значением true.
type Map struct {
// contains filtered or unexported fields
}
func (*Map) Clear 1.23
func (m *Map) Clear()
Clear удаляет все элементы, оставляя Map пустой.
func (*Map) CompareAndDelete 1.20
func (m *Map) CompareAndDelete(key, old any) (deleted bool)
CompareAndDelete удаляет элемент с ключом key, если его значение равно old. Значение old должно быть сравнимого типа.
Если в map нет текущего значения для key, CompareAndDelete возвращает false (даже если старое значение — значение интерфейса nil).
func (*Map) CompareAndSwap 1.20
func (m *Map) CompareAndSwap(key, old, new any) (swapped bool)
CompareAndSwap меняет местами старое и новое значения для key, если сохранённое в map значение равно old. Значение old должно быть сравнимого типа.
func (*Map) Delete 1.9
func (m *Map) Delete(key any)
Delete удаляет значение для ключа. Если ключа нет в map, Delete ничего не делает.
func (*Map) Load 1.9
func (m *Map) Load(key any) (value any, ok bool)
Load возвращает сохранённое в map значение для ключа или nil, если значения нет. Результат ok указывает, найдено ли значение в map.
func (*Map) LoadAndDelete 1.15
func (m *Map) LoadAndDelete(key any) (value any, loaded bool)
LoadAndDelete удаляет значение для ключа и возвращает предыдущее значение, если оно было. Результат loaded указывает, присутствовал ли ключ.
func (*Map) LoadOrStore 1.9
func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool)
LoadOrStore возвращает существующее значение для ключа, если оно есть. В противном случае функция сохраняет и возвращает заданное значение. Результат loaded равен true, если значение было загружено, и false, если оно было сохранено.
func (*Map) Range 1.9
func (m *Map) Range(f func(key, value any) bool)
Range последовательно вызывает f для каждого присутствующего в map ключа и значения. Если f возвращает false, итерация прекращается.
Range не обязательно отражает согласованный снимок содержимого Map: один и тот же ключ не будет посещён более одного раза, но если значение для какого-либо ключа одновременно сохраняется или удаляется (в том числе функцией f), Range может показать любое соответствие для этого ключа, существовавшее в любой момент выполнения вызова Range. Range не блокирует другие методы получателя; даже сама f может вызывать любые методы m.
Сложность Range может быть O(N) относительно числа элементов в map, даже если f возвращает false после постоянного числа вызовов.
func (*Map) Store 1.9
func (m *Map) Store(key, value any)
Store задаёт значение для ключа.
func (*Map) Swap 1.20
func (m *Map) Swap(key, value any) (previous any, loaded bool)
Swap заменяет значение для ключа и возвращает предыдущее значение, если оно было. Результат loaded указывает, присутствовал ли ключ.
type Mutex
Mutex — это блокировка взаимного исключения. Нулевое значение Mutex — незаблокированный мьютекс.
Mutex нельзя копировать после первого использования.
В терминологии модели памяти Go n-й вызов Mutex.Unlock «синхронизируется до» m-го вызова Mutex.Lock для любых n < m. Успешный вызов Mutex.TryLock эквивалентен вызову Lock. Неуспешный вызов TryLock вообще не устанавливает отношения «синхронизируется до».
type Mutex struct {
// contains filtered or unexported fields
}
func (*Mutex) Lock
func (m *Mutex) Lock()
Lock блокирует m. Если блокировка уже занята, вызывающая горутина приостанавливается до тех пор, пока мьютекс не освободится.
func (*Mutex) TryLock 1.18
func (m *Mutex) TryLock() bool
TryLock пытается заблокировать m и сообщает, удалось ли это.
Хотя корректные варианты использования TryLock существуют, они редки; использование TryLock часто указывает на более глубокую проблему в конкретном случае применения мьютексов.
func (*Mutex) Unlock
func (m *Mutex) Unlock()
Unlock разблокирует m. Если при входе в Unlock m не заблокирован, возникает ошибка времени выполнения.
Заблокированный Mutex не связан с какой-либо конкретной горутиной. Одна горутина может заблокировать Mutex, а затем организовать его разблокировку другой горутиной.
type Once
Once — это объект, который выполняет ровно одно действие.
Once нельзя копировать после первого использования.
В терминологии модели памяти Go возврат из f «синхронизируется до» возврата из любого вызова once.Do(f).
type Once struct {
// contains filtered or unexported fields
}
Пример
Код:
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for range 10 {
go func() {
once.Do(onceBody)
done <- true
}()
}
for range 10 {
<-done
}
Вывод:
Only once
func (*Once) Do
func (o *Once) Do(f func())
Do вызывает функцию f тогда и только тогда, когда Do вызывается для этого экземпляра Once впервые. Иными словами, если
var once Once
once.Do(f) вызывается несколько раз, только первый вызов запустит f, даже если при каждом вызове передаётся функция f с другим значением. Для выполнения каждой функции требуется новый экземпляр Once.
Do предназначен для инициализации, которую нужно выполнить ровно один раз. Поскольку f не принимает аргументов, может потребоваться использовать функциональный литерал, чтобы захватить аргументы функции, вызываемой через Do:
config.once.Do(func() { config.init(filename) })
Поскольку ни один вызов Do не возвращается, пока не завершится единственный вызов f, вызов Do из функции f приведёт к взаимной блокировке.
Если f вызывает панику, Do считает, что функция завершилась; последующие вызовы Do возвращаются, не вызывая f.
type Pool 1.3
Pool — это набор временных объектов, которые можно по отдельности сохранять и получать.
Любой элемент, сохранённый в Pool, может быть автоматически удалён в любой момент без уведомления. Если при этом Pool содержит единственную ссылку на элемент, он может быть освобождён.
Pool безопасно использовать одновременно из нескольких горутин.
Pool предназначен для кэширования выделенных, но не используемых объектов с целью их последующего повторного использования и снижения нагрузки на сборщик мусора. Иными словами, Pool упрощает создание эффективных потокобезопасных списков свободных объектов. Однако он подходит не для всех таких списков.
Подходящий вариант использования Pool — управление набором временных объектов, которые незаметно совместно используются и потенциально повторно применяются независимыми клиентами пакета, работающими одновременно. Pool позволяет распределить накладные расходы на выделение памяти между множеством клиентов.
Пример удачного применения Pool есть в пакете fmt, который поддерживает динамически изменяемое хранилище временных буферов вывода. Хранилище увеличивается под нагрузкой (когда многие горутины активно выполняют печать) и уменьшается при отсутствии активности.
С другой стороны, список свободных объектов, являющийся частью кратковременно живущего объекта, — неподходящий случай для Pool, поскольку в таком сценарии накладные расходы распределяются недостаточно эффективно. Таким объектам эффективнее реализовать собственный список свободных объектов.
Pool нельзя копировать после первого использования.
В терминологии модели памяти Go вызов Put(x) «синхронизируется до» вызова Pool.Get, который возвращает то же значение x. Аналогично, вызов New, возвращающий x, «синхронизируется до» вызова Get, возвращающего то же значение x.
type Pool struct {
// New optionally specifies a function to generate
// a value when Get would otherwise return nil.
// It may not be changed concurrently with calls to Get.
New func() any
// contains filtered or unexported fields
}
Пример
Код:
package sync_test
import (
"bytes"
"io"
"os"
"sync"
"time"
)
var bufPool = sync.Pool{
New: func() any {
// The Pool's New function should generally only return pointer
// types, since a pointer can be put into the return interface
// value without an allocation:
return new(bytes.Buffer)
},
}
// timeNow is a fake version of time.Now for tests.
func timeNow() time.Time {
return time.Unix(1136214245, 0)
}
func Log(w io.Writer, key, val string) {
b := bufPool.Get().(*bytes.Buffer)
b.Reset()
// Replace this with time.Now() in a real logger.
b.WriteString(timeNow().UTC().Format(time.RFC3339))
b.WriteByte(' ')
b.WriteString(key)
b.WriteByte('=')
b.WriteString(val)
w.Write(b.Bytes())
bufPool.Put(b)
}
func ExamplePool() {
Log(os.Stdout, "path", "/search?q=flowers")
// Output: 2006-01-02T15:04:05Z path=/search?q=flowers
}
func (*Pool) Get 1.3
func (p *Pool) Get() any
Get выбирает произвольный элемент из Pool, удаляет его из Pool и возвращает вызывающему коду. Get может проигнорировать пул и считать его пустым. Вызывающие не должны предполагать какую-либо связь между значениями, переданными в Pool.Put, и значениями, возвращаемыми Get.
Если Get в противном случае вернул бы nil, а p.New не равен nil, Get возвращает результат вызова p.New.
func (*Pool) Put 1.3
func (p *Pool) Put(x any)
Put добавляет x в пул.
type RWMutex
RWMutex — это блокировка взаимного исключения для чтения и записи. Блокировку может удерживать произвольное число читателей или один писатель. Нулевое значение RWMutex — незаблокированный мьютекс.
RWMutex нельзя копировать после первого использования.
Если какая-либо горутина вызывает RWMutex.Lock, пока блокировка уже удерживается одним или несколькими читателями, одновременные вызовы RWMutex.RLock будут заблокированы до тех пор, пока писатель не захватит и не освободит блокировку, чтобы обеспечить её доступность для писателя. Обратите внимание, что это запрещает рекурсивную блокировку для чтения. Нельзя повысить RWMutex.RLock до RWMutex.Lock или понизить RWMutex.Lock до RWMutex.RLock.
В терминологии модели памяти Go n-й вызов RWMutex.Unlock «синхронизируется до» m-го вызова Lock для любых n < m, как и для Mutex. Для любого вызова RLock существует такое n, что n-й вызов Unlock «синхронизируется до» этого вызова RLock, а соответствующий вызов RWMutex.RUnlock «синхронизируется до» вызова Lock с номером n+1.
type RWMutex struct {
// contains filtered or unexported fields
}
func (*RWMutex) Lock
func (rw *RWMutex) Lock()
Lock блокирует rw для записи. Если блокировка уже захвачена для чтения или записи, Lock ожидает её освобождения.
func (*RWMutex) RLock
func (rw *RWMutex) RLock()
RLock блокирует rw для чтения.
Не следует использовать этот метод для рекурсивной блокировки чтения: заблокированный вызов Lock не позволяет новым читателям захватывать блокировку. См. документацию к типу RWMutex.
func (*RWMutex) RLocker
func (rw *RWMutex) RLocker() Locker
RLocker возвращает интерфейс Locker, который реализует методы Locker.Lock и Locker.Unlock, вызывая rw.RLock и rw.RUnlock.
func (*RWMutex) RUnlock
func (rw *RWMutex) RUnlock()
RUnlock отменяет один вызов RWMutex.RLock; он не влияет на других одновременно работающих читателей. Если при входе в RUnlock rw не заблокирован для чтения, возникает ошибка времени выполнения.
func (*RWMutex) TryLock 1.18
func (rw *RWMutex) TryLock() bool
TryLock пытается заблокировать rw для записи и сообщает, удалось ли это.
Хотя корректные варианты использования TryLock существуют, они редки; использование TryLock часто указывает на более глубокую проблему в конкретном случае применения мьютексов.
func (*RWMutex) TryRLock 1.18
func (rw *RWMutex) TryRLock() bool
TryRLock пытается заблокировать rw для чтения и сообщает, удалось ли это.
Хотя корректные варианты использования TryRLock существуют, они редки; использование TryRLock часто указывает на более глубокую проблему в конкретном случае применения мьютексов.
func (*RWMutex) Unlock
func (rw *RWMutex) Unlock()
Unlock разблокирует rw для записи. Если при входе в Unlock rw не заблокирован для записи, возникает ошибка времени выполнения.
Как и в случае с Mutex, заблокированный RWMutex не связан с какой-либо конкретной горутиной. Одна горутина может вызвать RWMutex.RLock (RWMutex.Lock) для RWMutex, а затем организовать вызов RWMutex.RUnlock (RWMutex.Unlock) другой горутиной.
type WaitGroup
WaitGroup — это счётный семафор, обычно используемый для ожидания завершения группы горутин или задач.
Обычно главная горутина запускает задачи, каждую в новой горутине, вызывая WaitGroup.Go, а затем ожидает завершения всех задач, вызывая WaitGroup.Wait. Например:
var wg sync.WaitGroup wg.Go(task1) wg.Go(task2) wg.Wait()
WaitGroup также можно использовать для отслеживания задач без запуска новых горутин с помощью Go, используя WaitGroup.Add и WaitGroup.Done.
Предыдущий пример можно переписать, используя явно созданные горутины вместе с Add и Done:
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
task1()
}()
wg.Add(1)
go func() {
defer wg.Done()
task2()
}()
wg.Wait()
Этот шаблон часто встречается в коде, созданном до появления WaitGroup.Go.
WaitGroup нельзя копировать после первого использования.
type WaitGroup struct {
// contains filtered or unexported fields
}
Пример
В этом примере несколько URL-адресов загружаются одновременно; WaitGroup используется для ожидания завершения всех загрузок.
Код:
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.example.com/",
}
for _, url := range urls {
// Launch a goroutine to fetch the URL.
wg.Go(func() {
// Fetch the URL.
http.Get(url)
})
}
// Wait for all HTTP fetches to complete.
wg.Wait()
Пример (AddAndDone)
Этот пример эквивалентен основному примеру, но вместо Go использует Add/Done.
Код:
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.example.com/",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Decrement the counter when the goroutine completes.
defer wg.Done()
// Fetch the URL.
http.Get(url)
}(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()
func (*WaitGroup) Add
func (wg *WaitGroup) Add(delta int)
Add прибавляет delta, которое может быть отрицательным, к счётчику задач WaitGroup. Если счётчик становится равным нулю, все горутины, заблокированные на WaitGroup.Wait, разблокируются. Если счётчик становится отрицательным, Add вызывает панику.
Предпочтительно использовать WaitGroup.Go.
Обратите внимание: вызовы с положительным delta, выполняемые при нулевом значении счётчика, должны происходить до Wait. Вызовы с отрицательным delta или вызовы с положительным delta, начинающиеся при значении счётчика больше нуля, могут происходить в любой момент. Обычно это означает, что вызовы Add должны выполняться до оператора, создающего горутину, или другого ожидаемого события. Если WaitGroup повторно используется для ожидания нескольких независимых наборов событий, новые вызовы Add должны происходить после возврата всех предыдущих вызовов Wait. См. пример WaitGroup.
func (*WaitGroup) Done
func (wg *WaitGroup) Done()
Done уменьшает счётчик задач WaitGroup на единицу. Это эквивалентно Add(-1).
Предпочтительно использовать WaitGroup.Go.
В терминологии модели памяти Go вызов Done «синхронизируется до» возврата любого вызова Wait, который он разблокирует.
func (*WaitGroup) Go 1.25
func (wg *WaitGroup) Go(f func())
Go вызывает f в новой горутине и добавляет эту задачу в WaitGroup. Когда f возвращает управление, задача удаляется из WaitGroup.
Функция f не должна вызывать панику.
Если WaitGroup пуст, вызов Go должен произойти до WaitGroup.Wait. Обычно это означает, что Go вызывается для запуска задач до вызова Wait. Если WaitGroup не пуст, Go можно вызвать в любой момент. Это означает, что горутина, запущенная Go, может сама вызвать Go. Если WaitGroup повторно используется для ожидания нескольких независимых наборов задач, новые вызовы Go должны происходить после возврата всех предыдущих вызовов Wait.
В терминологии модели памяти Go возврат из f «синхронизируется до» возврата любого вызова Wait, который он разблокирует.
func (*WaitGroup) Wait
func (wg *WaitGroup) Wait()
Wait блокируется до тех пор, пока счётчик задач WaitGroup не станет равен нулю.
Подкаталоги
| Имя | Описание |
|---|---|
| .. | |
| atomic | Пакет atomic предоставляет низкоуровневые примитивы атомарной работы с памятью, полезные для реализации алгоритмов синхронизации. |
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/sync/