SoFunction
Updated on 2025-03-04

goland-sync/atomic atomic operation summary

Locks have been provided, why do we still need atomic atomic operation?

1. Locking is expensive, time-consuming, and requires context switching. Locking is implemented in the code layer, and the code is run in the user-state space. When operating the underlying layer, you need to switch from the user-state space to the kernel space, and then the kernel operates the underlying resources. Time-consuming

2. Atomic operations can be completed in the user state, and the performance is higher than that of mutex locks. Atomic operations are supported at the CPU level, and CPU can directly operate the underlying resources.

3. The steps required for atomic operation are simple, no need to add locks to unlock the steps

Why is atomic operation faster than mutex?

1. The atomic operation is fast because it depends on CPU instructions, not on external locks. No additional context switching
2. Atomic operations can ensure that the execution period is continuous and will not be interrupted (variables will not be modified by other means, and mutex may be modified by other means)

CAS is a CPU hardware synchronization primitive, and is the abbreviation of Compare And Swap (compare and exchange). In atomic operation, CAS, and in the sync/atomic package, all function names starting with ComparAndSwap are CAS operations.
CAS operations in go are implemented by borrowing atomic instructions provided by the CPU. When CAS operates to modify shared variables, there is no need to lock the shared variables, but checks them in a similar way to optimistic locks. The essence is to constantly occupy CPU resources for the overhead brought by locking (such as context switching time overhead).

Advantages of atomic operation:
Concurrently safe value replacement operations can be completed without forming critical areas and creating mutexes. This can greatly reduce the loss of synchronization to program performance.

Disadvantages of atomic operation:
When the value of the operation is frequently changed, the CAS operation is not so easy to succeed. Because the ild value needs to be matched, the next step of modification will be made only if the match is successful.

The current atomic package has the following atomic operations:
   Add,ComparAndSwap,Load,Store,Swap

4. The difference between mutex and atomic operations

Mutex lock purpose: Mutex locks are used to protect a piece of logic and ensure concurrency security. (such as operating database protection)
Atomic operation purpose: Atomic operation acts on the update protection of a variable to ensure concurrency security (for example, operating databases cannot operate atomically)

The underlying implementation of mutex: mutex is implemented by the scheduler of the operating system
The underlying implementation of atomic operations: directly supported by the underlying hardware instructions, which do not allow interruptions during execution. Therefore, atomic operations can ensure concurrency security without locks, and performance will be linearly expanded with the increase of CPUs.

5. Atomic operation method

5.1 atomic.AddInt32--Add

AddXXX, the naming method of the operation method is AddXXX, which ensures that the operands are atomic increase and decrease. The supported types are int32, int64, uint32, uint64, uintptr. When used, AddXXX is the corresponding operation method.

//addfunc demo() {
	var count int32 = 0
	atomic.AddInt32(&count, 10)
	(count) //10
}
//reducefunc demo() {
	var count int32 = 0
	atomic.AddInt32(&count, -10)
	(count) //-10
}

Comparison of lock and atomic operations:

//Mutex lockfunc demo1() {
	sta := ().Unix()
	count := 0
	mux := {}
	wg := {}
	for i := 0; i < 10000; i++ {
		(1)
		go func() {
			defer ()
			for j := 0; j < 10000; j++ {
				()
				count++
				()
			}
		}()
	}
	()
	(count) //100000000
	(().Unix() - sta) //10 seconds}

//atomic atomic operation: more than 2 times fasterfunc demo2() {
	sta := ().Unix()
	wg := {}
	var count int32 = 0
	for i := 0; i < 10000; i++ {
		(1)
		go func() {
			defer ()
			for j := 0; j < 10000; j++ {
				atomic.AddInt32(&count, 1)
			}
		}()
	}
	()
	(count) //100000000
	(().Unix() - sta) //4 seconds}

5.2 CAS-atomic.CompareAndSwapInt32--Compare and replace

CompareAndSwap: Compare and replace, similar to optimistic lock, first compare whether the old value is consistent with the current value. If the same is true, replace the new value.
The operation method is named CompareAndSwapXXX

//true
func demo3() {
	var count int32 = 0
	boo := atomic.CompareAndSwapInt32(&count, 0, 100)
	(count) //100
	(boo)   //true
}


//false
func demo3() {
	var count int32 = 0
	boo := atomic.CompareAndSwapInt32(&count, 10, 100)
	(count) //0
	(boo) //false
}

5.3 atomic.StoreInt32--write operation

func demo3() {
	var count int32 = 0
	atomic.StoreInt32(&count, 666)
	(count) //666
}

5.4 atomic.LoadInt32--read operation

func demo3() {
	var count int32 = 0
	atomic.StoreInt32(&count, 666)

	val := atomic.LoadInt32(&count)
	(val) //666
}

5.5 atomic.SwapInt32--direct exchange

atomic.SwapInt32:Direct exchange,and return the value before the exchange

func demo3() {
	var count int32 = 0
	old := atomic.SwapInt32(&count, 100)
	(old)   //0
	(count) //100
}

This is the end of this article about goland-sync/atomic atomic operation. For more related goland sync/atomic atomic operation content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!