An atomic operation is an operation that cannot be interrupted during the process. Atomic operation for a value in the process of being carried out, the CPU will never go to another operation for that value.
To achieve this rigor, atomic operations are represented and completed only by a separate CPU instruction.
- The Go language provides atomic operations that are non-invasive, represented by a number of functions in the standard library sync/atomic
- Types include Int32,int64,uint32,uint64,uintptr,unsafe. Pointer, a total of six.
- These functions provide a total of five atomic operations: increase or decrease, compare and exchange, load, store and exchange
int various types of value range
type |
Length (bytes) |
Value Range |
int8 |
1 |
-128 ~ 127 |
Uint8 (Byte) |
1 |
0 ~ 255 |
Int16 |
2 |
32768~32767 |
UInt16 |
2 |
0~65535 |
Int32 |
4 |
2147483648~2147483647 |
UInt32 |
4 |
0~4294967295 |
Int64 |
8 |
-9223372036854775808~9223372036854775807 |
UInt64 |
8 |
0~18446744073709551615 |
Int |
Platform-related |
Platform-related |
UInt |
Platform-related |
Platform-related |
UIntPtr |
Same pointer |
32 bits flat down to 4 bytes, 64 bits flat down to 8 bytes |
Increase or decrease add
The function name is prefixed with add and followed by the name of the specific type.
- The type being manipulated can only be numeric type
- Int32,int64,uint32,uint64,uintptr types can use atomic increment or decrement operations
- The first parameter value must be a pointer-type value to impose a special CPU instruction
- The type of the second parameter value is always the same as the type of the first manipulated value.
Example
PackageMainImport("FMT" "Sync/atomic")funcMain () {varI32Int32Fmt. Println ("=====old i32 value=====") FMT. Println (I32)The first parameter value must be a pointer-type value, because the function needs to obtain the position of the manipulated value in memory in order to impose a special CPU instruction //End returns the new value after atomic operationNEWI32: = Atomic. AddInt32 (&i32, 3) FMT. Println ("=====new i32 value=====") FMT. Println (I32) fmt. Println (NEWI32)varI64Int64Fmt. Println ("=====old i64 value=====") FMT. Println (i64) newI64: = Atomic. AddInt64 (&i64, -3) FMT. Println ("=====new i64 value=====") FMT. Println (i64) fmt. Println (NEWI64)}
Results:
/usr/local/go/bin/go Run/users/liuxinming/go/src/free/learngo/atomic/add/add.go
=====old i32 value=====
0
=====new i32 value=====
-3
-3
=====old i64 value=====
0
=====new i64 value=====
-3
-3
Compare and Exchange CAs
Compare and swaps are referred to as CAs, and in sync/atomic packages, such atomic operations are represented by a number of functions prefixed by the name ' Compareandswap '.
func CompareAndSwapInt32(addr *int32newint32bool)
- When the function is called, the value of the manipulated value pointed to by the parameter addr is equal to that of the parameter old.
- The old value is replaced with the new value represented by the parameter new only if the result is positive, otherwise the operation is ignored.
So, it needs to be tried continuously with a for loop until it succeeds
The practice of using locks tends to be pessimistic
- We always assume that there will be concurrent operations to modify the value of the operation and to use the lock to secure the relevant operation into the critical section
- The practice of using CAS tends to be optimistic
- Always assume that the manipulated value has not been changed (that is, equal to the old value) and that the value is replaced as soon as the authenticity of the hypothesis is confirmed.
Example
PackageMainImport("FMT" "Sync/atomic")varValueInt32funcMain () {FMT. Println ("======old value=======") FMT. Println (value) fmt. Println ("======cas value=======") AddValue(3) FMT. Println (Value)}//continually attempt to update the value of values atomically, until the operation succeedsfuncAddValue (DeltaInt32){//The CAS operation is not so easy to succeed in cases where the manipulated values are frequently changed //so had to use a for loop for multiple attempts for{V: = valueifAtomic.compareandswapint32 (&value, V, (v + delta)) {//When the result value of the function is true, exit the loop Break}the reason for the failure of the operation is always that the old value of value is not equal to the value of V. Although the//cas operation does not allow a goroutine to block on a statement, it may temporarily stop the execution of the abortion, but most of the time is extremely short.}}
Results:
======old value=======
0
======cas value=======
3
Load load
The above comparison and Exchange case total v:= value for variable v assignment, but ... It is important to note that during the operation of reading value, other read and write operations on this value can be performed concurrently, and the read operation is likely to read a data that is only half modified.
- So, we're going to use the Sync/atomic code package to also provide us with a series of functions that are prefixed with load (load) to ensure that such a bad thing happens.
Example
PackageMainImport("FMT" "Sync/atomic")varValueInt32funcMain () {FMT. Println ("======old value=======") FMT. Println (value) fmt. Println ("======cas value=======") AddValue(3) FMT. Println (Value)}//continually attempt to update the value of values atomically, until the operation succeedsfuncAddValue (DeltaInt32){//The CAS operation is not so easy to succeed in cases where the manipulated values are frequently changed //so had to use a for loop for multiple attempts for{//v: = value //In the process of reading value, other read and write operations on this value can be performed concurrently, and the read operation is likely to read a data that is only half modified. ///So we're going to use loadV: = Atomic. LoadInt32 (&value)ifAtomic.compareandswapint32 (&value, V, (v + delta)) {//When the result value of the function is true, exit the loop Break}the reason for the failure of the operation is always that the old value of value is not equal to the value of V. Although the//cas operation does not allow a goroutine to block on a statement, it may temporarily stop the execution of the abortion, but most of the time is extremely short.}}
- Atomic. LoadInt32 accepts a pointer value of type *int32
- Returns the value that the pointer points to
Storage Store
The write operation corresponds to the read operation. The Sync/atomic package also provides a stored function of the value of the atom corresponding to the load function of the atom. The store is prefixed with
- In the process of storing a value atomically, any CPU does not perform a read or write operation on the same value.
- The atomic value store operation is always successful because it does not care what the old value of the manipulated value is
- differs significantly from CAS operations
fmt.Println("======Store value=======") atomic.StoreInt32(&value, 10) fmt.Println(value)
Swap swap
- Unlike CAS operations, atomic Exchange operations do not care about the old values being manipulated.
- It will set the new value directly
- It returns the old value of the manipulated value
- Such operations are less restrictive than CAS operations and are more powerful than atomic loading operations
Actual case (continue to transform the previous version of the code)
//Data file implementation typetypeMydatafilestruct{f *os. File//DocumentsFmutex sync. Rwmutex//read-write lock used for fileRcond *sync. Cond//The condition variable to be used for the read operationWoffsetInt64 //The offset to be used for write operationsRoffsetInt64 //The offset to be used for the read operationDatalenUInt32 //Data block length}//omitted here ...func(DF *mydatafile) Read () (RSNInt64, d Data, err error) {//Read and update read offset varOffsetInt64 for{offset = atomic. LoadInt64 (&df.roffset)ifAtomic.compareandswapint64 (&df.roffset, offset, offset +Int64(Df.datalen))) { Break} }//Read a data block, last read block serial numberRSN = offset/Int64(df.datalen) Bytes: = Make([]byte, Df.datalen)//Read/write Lock: Read lockDf.fmutex.RLock ()deferDf.fmutex.RUnlock () for{_, Err = Df.f.readat (bytes, offset)ifErr! =Nil{ifErr = = Io. EOF {//Temporarily discard Fmutex's read lock and wait for notification to arriveDf.rcond.Wait ()Continue} } Break} d = bytesreturn}func(DF *mydatafile) Write (d Data) (WSNInt64, err Error) {//Read and update the write offset varOffsetInt64 for{offset = atomic. LoadInt64 (&df.woffset)ifAtomic.compareandswapint64 (&df.woffset, offset, offset +Int64(Df.datalen))) { Break} }//write a data block and write the serial number of the data block lastWSN = offset/Int64(Df.datalen)varbytes []byte if Len(d) >int(Df.datalen) {bytes = d[0:d F.datalen]}Else{bytes = d} df.fmutex.Lock ()deferDf.fmutex.Unlock () _, err = Df.f.write (bytes)//Send notificationsDf.rcond.Signal ()return}func(DF *mydatafile) RSN ()Int64{offset: = atomic. LoadInt64 (&df.roffset)returnOffset/Int64(Df.datalen)}func(DF *mydatafile) WSN ()Int64{offset: = atomic. LoadInt64 (&df.woffset)returnOffset/Int64(Df.datalen)}
Complete code on GitHub:
Https://github.com/lxmgo/learngo
Some of the learning process to organize, I hope you learn the go language to help.
Golang synchronization: Atomic operation using