Пакет 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
func OnceValue[T any](f func() T) func() T
OnceValue возвращает функцию, которая вызывает f только один раз и возвращает значение, возвращённое f. Возвращённую функцию можно вызывать параллельно.
Если f вызывает панику, то возвращённая функция будет вызывать панику с тем же значением при каждом вызове.
Пример
В этом примере OnceValue используется для выполнения «дорогостоящего» вычисления только один раз, даже при параллельном использовании.
Код:
once := sync.OnceValue(func() int {
sum := 0
for i := 0; i < 1000; i++ {
sum += i
}
fmt.Println("Computed once:", sum)
return sum
})
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
const want = 499500
got := once()
if got != want {
fmt.Println("want", want, "got", got)
}
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
Вывод:
Computed once: 499500
func OnceValues
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 i := 0; i < 10; i++ {
go func() {
data, err := once()
if err != nil {
fmt.Println("error:", err)
}
_ = data // Ignore the data for this example
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
Вывод:
Reading file once
тип Cond
Cond реализует переменную условия, точку встречи для горутин, ожидающих или объявляющих событие.
Каждая Cond имеет связанную Locker L (часто *Mutex или *RWMutex), которая должна быть захвачена при изменении условия и при вызове метода Cond.Wait.
Cond не должен копироваться после первого использования.
В терминологии модели памяти Go, Cond обеспечивает, что вызов Cond.Broadcast или Cond.Signal “синхронизируется до” любого вызова Wait, который он разблокирует.
Для многих простых случаев использования пользователи будут лучше работать с каналами, чем с Cond (Broadcast соответствует закрытию канала, а Signal соответствует отправке в канал).
Подробнее о замене sync.Cond см. в серии статей Роберто Клаписа о продвинутых шаблонах параллельности https://blogtitle.github.io/categories/concurrency/, а также в докладе Брайана Миллса о шаблонах параллельности https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view.
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 во время вызова.
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.
Поскольку c.L не захвачен во время ожидания Wait, вызывающий, как правило, не может предположить, что условие истинно, когда Wait возвращает значение. Вместо этого вызывающий должен ожидать в цикле:
c.L.Lock()
for !condition() {
c.Wait()
}
... make use of condition ...
c.L.Unlock()
тип Locker
Locker представляет собой объект, который можно захватить и разблокировать.
type Locker interface {
Lock()
Unlock()
} тип Map 1.9
Map похож на Go map[any]any, но безопасен для одновременного использования несколькими горутинами без дополнительных блокировок или координации. Загрузки, сохранения и удаления выполняются за амортизированное постоянное время.
Тип 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 удаляет запись для ключа, если его значение равно old. Значение old должно быть типа, допускающего сравнение.
Если в карте нет текущего значения для ключа, CompareAndDelete возвращает false (даже если значение old является значением nil интерфейса).
func (*Map) CompareAndSwap 1.20
func (m *Map) CompareAndSwap(key, old, new any) (swapped bool)
CompareAndSwap меняет старое и новое значения для ключа, если значение, хранящееся в карте, равно old. Значение old должно быть типа, допускающего сравнение.
func (*Map) Delete 1.9
func (m *Map) Delete(key any)
Delete удаляет значение для ключа.
func (*Map) Load 1.9
func (m *Map) Load(key any) (value any, ok bool)
Load возвращает значение, хранящееся в карте для ключа, или nil, если значение отсутствует. Результат ok указывает, найдено ли значение в карте.
func (*Map) LoadAndDelete 1.15
func (m *Map) LoadAndDelete(key any) (value any, loaded bool)
LoadAndDelete удаляет значение для ключа, возвращая предыдущее значение, если оно есть. Загруженный результат сообщает, был ли ключ присутствующим.
func (*Map) LoadOrStore 1.9
func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool)
LoadOrStore возвращает существующее значение для ключа, если оно присутствует. В противном случае он сохраняет и возвращает заданное значение. Загруженный результат — true, если значение было загружено, и false, если оно было сохранено.
func (*Map) Range 1.9
func (m *Map) Range(f func(key, value any) bool)
Range последовательно вызывает f для каждого ключа и значения, присутствующего в карте. Если f возвращает false, цикл перебора останавливается.
Range не обязательно соответствует какому-либо согласованному снимку содержимого карты: ни один ключ не будет посещен более одного раза, но если значение для любого ключа сохраняется или удаляется одновременно (включая f), Range может отражать любое отображение для этого ключа с любой точки во время вызова Range. Range не блокирует другие методы на получателе; даже сам f может вызвать любой метод на m.
Range может быть O(N) с количеством элементов в карте, даже если 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 меняет значение для ключа и возвращает предыдущее значение, если оно есть. Загруженный результат сообщает, был ли ключ присутствующим.
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. Это ошибка во время выполнения, если m не заблокирован при входе в Unlock.
Заблокированный 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 i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-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, если f вызывает Do, он приведет к тупику.
Если f вызывает ошибку, Do считает, что она вернулась; будущие вызовы Do возвращаются без вызова f.
type Pool 1.3
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 и рассматривать его как пустой. Вызывающие стороны не должны предполагать никакой связи между значениями, переданными в Pool.Put, и значениями, возвращаемыми Get.
Если Get в противном случае вернул бы nil, а p.New не равен nil, Get возвращает результат вызова p.New.
func (*Pool) Put 1.3
func (p *Pool) Put(x any)
Put добавляет x в Pool.
type RWMutex
RWMutex — это мьютекс чтения/записи. Блокировка может удерживаться произвольным количеством читателей или одним писателем.
Нулевое значение для RWMutex — это разблокированный мьютекс.
RWMutex нельзя копировать после первого использования.
Если какая-либо горутина вызывает RWMutex.Lock, в то время как блокировка уже удерживается одним или несколькими читателями, одновременные вызовы RWMutex.RLock будут блокироваться, пока писатель не получит (и не освободит) блокировку, чтобы гарантировать, что блокировка в конечном итоге станет доступной для писателя. Обратите внимание, что это запрещает рекурсивную блокировку чтения.
В терминологии модели памяти Go, n-ый вызов RWMutex.Unlock «синхронизируется до» m-го вызова Lock для любых n < m, точно так же, как и для Mutex. Для любого вызова RLock существует n, такой что n-ый вызов Unlock «синхронизируется до» этого вызова RLock, а соответствующий вызов RWMutex.RUnlock «синхронизируется до» n+1-го вызова Lock.
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; он не влияет на других одновременных читателей. Это ошибка во время выполнения, если rw не заблокирован для чтения при входе в RUnlock.
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 для записи. Это ошибка во время выполнения, если rw не заблокирован для записи при входе в Unlock.
Как и в случае с Mutex, заблокированный RWMutex не связан с конкретной горутиной. Одна горутина может RWMutex.RLock (RWMutex.Lock) RWMutex, а затем организовать разблокировку другой горутиной RWMutex.RUnlock (RWMutex.Unlock) его.
type WaitGroup
WaitGroup ожидает завершения коллекции горутин. Основная горутина вызывает WaitGroup.Add, чтобы установить количество горутин, ожидающих завершения. Затем каждая из горутин выполняется и вызывает WaitGroup.Done, когда завершается. Одновременно WaitGroup.Wait может быть использована для блокировки до тех пор, пока все горутины не завершатся.
WaitGroup нельзя копировать после первого использования.
В терминологии модели памяти Go, вызов WaitGroup.Done «синхронизируется до» возврата любого вызова Wait, который он разблокирует.
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 {
// 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 вызывает панику.
Обратите внимание, что вызовы с положительным значением дельты, которые происходят, когда счётчик равен нулю, должны происходить до вызова Wait. Вызовы с отрицательным значением дельты или вызовы с положительным значением дельты, которые начинаются, когда счётчик больше нуля, могут происходить в любое время. Как правило, это означает, что вызовы Add должны выполняться до оператора создания горутины или другого ожидаемого события. Если WaitGroup используется повторно для ожидания нескольких независимых наборов событий, новые вызовы Add должны выполняться после возвращения всех предыдущих вызовов Wait. См. пример WaitGroup.
func (*WaitGroup) Done
func (wg *WaitGroup) Done()
Done уменьшает счётчик WaitGroup на единицу.
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/