Эта серия статей знакомит со многими методами, которые часто используются в параллельном программировании, за исключениемContext
, таймеры, мьютексы, и есть техника вне канала --атомарная операцияИспользуется в некоторых алгоритмах синхронизации. В сегодняшней статье мы кратко разберемсяGo
Поддержка атомарных операций в языке, а затем обсуждение разницы между атомарными операциями и мьютексами.
Основные темы статьи следующие:
- атомарная операция
-
Go
Поддержка атомарных операций - Разница между атомарной операцией и мьютексом
атомарная операция
Атомарная операция — это операция, которая не может быть прервана во время процесса.Во время выполнения атомарной операции над определенным значением ЦП никогда не будет выполнять другие операции над значением. Чтобы достичь такого уровня строгости, атомарные операции представляются и выполняются только одной инструкцией ЦП. Атомарные операции не требуют блокировки и часто реализуются непосредственно с помощью инструкций ЦП. На самом деле реализации других методов синхронизации часто полагаются на атомарные операции.
Перейти на поддержку атомарных операций
Перейти на языкsync/atomic
Пакет обеспечивает поддержку атомарных операций для синхронизации доступа к целым числам и указателям.
- Атомарные операции, предоставляемые языком Go, не являются навязчивыми.
- Эти функции обеспечивают пять атомарных операций: сложение и вычитание, сравнение и обмен, загрузка, сохранение и обмен.
- Типы, поддерживаемые атомарными операциями, включают int32, int64, uint32, uint64, uintptr, unsafe.Pointer.
В следующем примере показано, как использоватьAddInt32
Функция выполняет атомарную операцию добавления над значением int32. В этом примереmain goroutine
Создано 1000 одновременныхgoroutine
. каждый вновь созданныйgoroutine
Увеличьте целое число n на 1.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var n int32
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
atomic.AddInt32(&n, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(atomic.LoadInt32(&n)) // output:1000
}
В приведенном выше примере вы можете проверить это самостоятельно, если мы не используемatomic.AddInt32(&n, 1)
Вместо этого просто соедините переменнуюn
Результат, полученный автоинкрементом, не тот 1000, который мы ожидали, что мы и получили в статье "Гонки данных и решения в параллельном программировании на Go», упомянутых в проблеме гонки данных, атомарные операции могут обеспечить этиgoroutine
Между ними нет гонки данных.
Сравните и поменяйте местами атомарные операции для краткостиCAS
(Сравнить и поменять местами), вsync/atomic
package, такие атомарные операции представлены именами, начинающимися сCompareAndSwap
Предоставляет несколько функций для префикса
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer,old, new unsafe.Pointer) (swapped bool)
......
После вызова функцииCompareAndSwap
Функция сначала определит параметрыaddr
Значение операции и параметры, на которые указываютold
равны ли значения, только если результат этого сужденияtrue
После этого будут использоваться параметрыnew
Представленное новое значение заменяет старое значение, в противном случае операция игнорируется.
мы используемmutex
Мьютексы похожи на пессимистические блокировки.Всегда предполагается, что будут параллельные операции по изменению обрабатываемого значения, поэтому блокировки используются для размещения связанных операций в критических секциях для защиты. при использованииCAS
Операции имеют тенденцию к оптимистичной блокировке, всегда предполагая, что обрабатываемое значение не изменилось (т. е. равно старому значению), и выполняя замену значения, как только подтверждается правильность этого предположения. Когда управляемое значение часто изменяется,CAS
Операцию не так просто выполнить, поэтому вам нужно продолжать попытки, пока не добьетесь успеха.
package main
import (
"fmt"
"sync/atomic"
)
var value int32 = 1
func main() {
fmt.Println("======old value=======")
fmt.Println(value)
addValue(10)
fmt.Println("======New value=======")
fmt.Println(value)
}
//不断地尝试原子地更新value的值,直到操作成功为止
func addValue(delta int32){
for {
v := value
if atomic.CompareAndSwapInt32(&value, v, (v + delta)){
break
}
}
}
В приведенном выше случае сравнения и обменаv:= value
как переменнаяv
задание, но имейте в виду, что при чтенииvalue
Во время операции другие операции чтения и записи для этого значения могут выполняться одновременно, поэтому эта операция чтения, скорее всего, будет считывать данные, которые были изменены только наполовину. Итак, мы собираемся использоватьsync/atomic
Пакет кода предоставляет намLoad
префиксные функции, чтобы избежать таких плохих вещей.
Состояние гонки вызвано асинхронным доступом к общему ресурсу и попыткой чтения и записи ресурса одновременно.Идея использования мьютекса и канала состоит в том, чтобы заблокировать доступ других потоков к общей памяти после того, как поток получит право доступа , и используйте атомарную операцию. Операция решает проблему гонки данных, используя ее непрерывный характер.
оatomic
Для более подробного ознакомления с использованием пакета вы можете посетить официальныйсинхронизация/атомарная китайская документация
Разница между атомарной операцией и мьютексом
Мьютекс — это структура данных, позволяющая выполнять ряд взаимоисключающих операций. В то время как атомарная операция — это единственная операция, которая является взаимоисключающей, что означает, что никакой другой поток не может ее прервать. тогдаGo
на языкеatomic
Атомарные операции в пакетах иsync
В чем разница между блокировками синхронизации, предоставляемыми пакетом?
во-первыхatomic
Преимущество операции в том, что она легче, напримерCAS
Операции замены значений с защитой от параллелизма можно выполнять без формирования критических секций и создания мьютексов. Это может значительно уменьшить потерю синхронизации при работе программы.
Атомарные операции также имеют недостатки. или сCAS
В качестве примера используйте операциюCAS
Практика эксплуатации склонна к оптимизму, всегда предполагая, что оперируемое значение не было изменено (то есть равно старому значению), и как только подтверждается подлинность этого предположения, значение немедленно заменяется, а затем в случай, когда действующее значение часто изменяется,CAS
Операция не так-то просто увенчалась успехом. Практика использования мьютекса имеет тенденцию быть пессимистичной: мы всегда предполагаем, что будут параллельные операции для изменения значения операции, и используем блокировки для размещения связанных операций в критических секциях для защиты.
Итак, если подытожить разницу между атомарными операциями и блокировками мьютексов, то:
- Мьютекс — это структура данных, позволяющая потоку выполнять критическую часть программы для выполнения нескольких взаимоисключающих операций.
- Атомарная операция — это единственная взаимоисключающая операция над значением.
- Блокировки взаимного исключения можно понимать как пессимистические блокировки: общие ресурсы используются только одним потоком за раз, другие потоки блокируются, а ресурсы передаются другим потокам после их использования.
atomic
Пакет предоставляет низкоуровневые примитивы атомарной памяти, полезные для реализации алгоритмов синхронизации. Эти функции нужно использовать очень осторожно. Неправильное использование увеличит накладные расходы системных ресурсов. Для прикладного уровня лучше использовать каналы илиsync
Функции, предусмотренные в пакете, для завершения операции синхронизации.
противatomic
Точка зрения сумки также много обсуждается в группе рассылки Google, и один из выводов объясняется:
Этот пакет следует избегать. Кроме того, прочитайте главу «Атомарные операции» стандарта C++11; если вы понимаете, как безопасно использовать эти операции в C++, то вы можете безопасно использовать Go
sync/atomic
возможность упаковки.
Рекомендуемое чтение
Идеи решения проблем и планировщик языка Go для параллельных задач