[Go] Detailed sync/atomic package

Table of contents

sync/atomic package


sync/atomic package

Atomic operations (Atomic Operations) refer to concurrent programming, which can ensure that the operations on shared variables are indivisible and atomic in a multi-threaded environment. They can avoid race conditions (Race Condition) and data race (Data Race), and ensure the atomicity and thread safety of operations.

The Go language sync/atomicpackage provides some atomic operation functions, and the commonly used ones are as follows:

  • Add: Atomically adds the specified integer to an address and returns the updated value.
func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
  • Load: Atomically reads the value at the specified address and returns the value read.
func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
  • Store: Atomically stores the specified value at an address.
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
  • CompareAndSwap: Compare whether the value at the specified address is equal to the old value, if they are equal, store the new value at the address atomically, and return whether the exchange has been performed.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)

In addition to the above functions, there are many other atomic manipulation functions such as increment, decrement, bit manipulation, etc. These atomic operation functions can guarantee the atomicity of operations in a concurrent environment, avoiding race conditions and data competition.

It should be noted that atomic operations can only be used for primitive types and pointer types. MutexFor complex types or operations that need to ensure the consistency of multiple variables, mutex locks ( ) or read-write locks ( ) can be used in combination RWMutexto achieve thread safety.

In concurrent programming, when multiple threads or coroutines access and modify shared variables at the same time, race conditions (Race Condition) and data races (Data Race) may occur. A race condition means that multiple threads compete to operate on the same resource, resulting in the correctness of the result depending on the randomness of the execution order; a data race means that multiple threads perform read and write operations on the same variable, and at least one operation is a write operation, resulting in a memory access violation and undefined behavior.

Atomic operations provide a way to avoid race conditions and data races, ensuring that operations on shared variables are indivisible and atomic. Specifically, atomic operations have the following important meanings:

  1. Atomic operations: Atomic operations can ensure that operations on shared variables are indivisible unit operations. At any moment, only one thread can perform an atomic operation, and other threads need to wait for the completion of the atomic operation before proceeding. This avoids conflicts and erroneous results caused by multiple threads modifying shared variables at the same time.

  2. Thread Safety: Atomic operations are thread safe, ensuring data correctness even under high concurrency. The consistency of shared variables can be maintained through atomic operations, avoiding data inconsistency and undefined behavior caused by concurrent access.

  3. Synchronization mechanism: Atomic operations provide a lightweight synchronization mechanism that does not require explicit locks (such as mutexes) to protect shared variables. Atomic operations generally have lower overhead and higher performance than traditional locking mechanisms.

  4. Concurrency Safety: Atomic operations are concurrency safe and do not require additional synchronization mechanisms. In a multi-threaded or coroutine environment, race conditions and data races can be avoided through atomic operations, providing a simple and effective way to achieve concurrency safety.

In general, atomic operations play a role in protecting shared variables in concurrent programming, avoiding race conditions and data competition, and providing an efficient and reliable way to deal with concurrent access problems between multi-threads or coroutines . In the Go language, sync/atomicthe package provides a series of atomic operation functions, which is convenient for developers to use and manage atomic operations.

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	x      int64
	wg     sync.WaitGroup
	rwlock sync.RWMutex
)

func write() {
	// lock.Lock()   // 加互斥锁
	rwlock.Lock() // 加写锁
	x = x + 1
	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
	rwlock.Unlock()                   // 解写锁
	// lock.Unlock()                     // 解互斥锁
	wg.Done()
}

func read() {
	// lock.Lock()                  // 加互斥锁
	rwlock.RLock()               // 加读锁
	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
	rwlock.RUnlock()             // 解读锁
	// lock.Unlock()                // 解互斥锁
	wg.Done()
}

func main() {
	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go write()
	}

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go read()
	}

	wg.Wait()
	end := time.Now()
	fmt.Println(end.Sub(start))
}

Guess you like

Origin blog.csdn.net/fanjufei123456/article/details/131116458