In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly shows you "what atomic operations Golang provides", the content is easy to understand, clear, hope to help you solve your doubts, the following let the editor lead you to study and learn what atomic operations Golang provides "this article.
What atomic operations are provided by the Go language
The Go language provides support for atomic operations through the built-in package sync/atomic, which provides the following main categories of atomic operations:
To add or subtract, the operation method is named AddXXXType, which ensures atomic addition or subtraction of operands. The supported types are int32, int64, uint32, uint64 and uintptr. Replacing the XXXType I mentioned earlier with the actual type is the corresponding operation method.
Loading ensures that no other task changes the Operand before it is read. The operation method is named LoadXXXType, and the supported types support Pointer in addition to the base type, that is, loading pointers of any type.
Storage, if there is a load, there must be a storage operation. The method name of this type of operation starts with Store and supports the same types as those supported by the load operation.
Compare and exchange, that is, CAS (Compare And Swap), many concurrency primitive implementations like Go rely on CAS operations, which also support the types listed above.
Exchange, this is simple and rough, not more direct exchange, this operation is rarely used.
The difference between mutex and atomic operation
On weekdays, in concurrent programming, the synchronization primitive Mutex in the Go language sync package is often used to ensure concurrency security, so what's the difference between it and the atomic package? In my opinion, they are different in terms of usage purpose and underlying implementation:
Purpose of use: mutexes are used to protect a piece of logic, and atomic operations are used to protect a variable from updates.
Underlying implementation: Mutex is implemented by the scheduler of the operating system, while the atomic operations in the atomic package are directly supported by the underlying hardware instructions, which are not allowed to be interrupted in the process of execution, so atomic operations can ensure concurrency security in the case of lock-free, and its performance can scale linearly with the increase of the number of CPU.
For the protection of a variable update, atomic operations are usually more efficient and can take advantage of the multi-core advantages of computers.
For example, the following is a concurrent counter program that uses mutexes:
Func mutexAdd () {var an int32 = 0 var wg sync.WaitGroup var mu sync.Mutex start: = time.Now () for I: = 0; I < 100000000 Time.Now + {wg.Add (1) go func () {defer wg.Done () mu.Lock () a + = 1 mu.Unlock ()} ()} wg.Wait () timeSpends: = time.Now () .Sub (start) .Nanoseconds () fmt.Printf ("use mutex an is% d, spend time:% v\ n", a, timeSpends)}
Changing Mutex to using the method atomic.AddInt32 (& a, 1) calls still ensures concurrency security for variable increments without locking.
Func AtomicAdd () {var an int32 = 0 var wg sync.WaitGroup start: = time.Now () for I: = 0; I < 1000000; iTunes + {wg.Add (1) go func () {defer wg.Done () atomic.AddInt32 (& a, 1)} ()} wg.Wait () timeSpends: = time.Now (). Sub (start). Nanoseconds () fmt.Printf ("use atomic an is% d, spend time:% v\ n", atomic.LoadInt32 (& a), timeSpends)}
You can run the above two pieces of code locally, and you can observe that the result of the counter is 1000000 in the end, which is thread-safe.
It should be noted that the Operand parameters of all atomic operations must be pointer types, and the address of the operands in memory can be obtained through pointer variables, thus imposing special CPU instructions to ensure that only one goroutine can operate at a time.
In the above example, in addition to adding operations, we also demonstrate the loading operation, so let's take a look at the CAS operation.
Compare and exchange
This operation is referred to as CAS (Compare And Swap). This type of operation is prefixed with CompareAndSwap:
Func CompareAndSwapInt32 (addr * int32, old, new int32) (swapped bool) func CompareAndSwapPointer (addr * unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
Before the exchange, the operation ensures that the value of the Operand has not been changed, that is, the value recorded by the parameter old is still saved, and the exchange operation is carried out only if this prerequisite is met. The practice of CAS is similar to the optimistic locking mechanism that is common when manipulating databases.
It is important to note that when there are a large number of goroutine to read and write variables, the CAS operation may not be successful, and you can use the for loop to try many times.
I only listed the typical CAS methods of int32 and unsafe.Pointer types above. I mainly want to say that in addition to the comparison and exchange of read value types, I also support the comparison and exchange of pointers.
Unsafe.Pointer provides a way to bypass the restrictions on pointer types in the Go language. Unsafe does not mean that it is not safe, but that there is no official guarantee of backward compatibility.
/ / define a pointer of struct type Ptype P struct {x, y, z int} / / execute type P pointer var pP * P func main () {/ / define a pointer variable var unsafe1 = (* unsafe.Pointer) (unsafe.Pointer (& pP)) / / Old pointer var sy P / / set unsafe1 to Old Pointer px: = atomic.SwapPointer (unsafe1) to demonstrate the effect Unsafe.Pointer (& sy) / / execute CAS operation If the exchange is successful, the result returns true y: = atomic.CompareAndSwapPointer (unsafe1, unsafe.Pointer (& sy), px) fmt.Println (y)}
The above example is not a CAS in a concurrent environment, but just to demonstrate the effect, first set the Operand to Old Pointer.
In fact, the underlying implementation of Mutex also relies on the CAS implementation of atomic operations, which is equivalent to the implementation dependence of the synchronization primitives in the sync package.
For example, the structure of a mutex Mutex has a state field that represents the status bit of the lock.
Type Mutex struct {state int32 sema uint32}
To make it easier to understand, we define its state here as 0 and 1, 0 means that the lock is currently idle, 1 means it has been locked, and the following is part of the implementation code of the Lock method in sync.Mutex.
Func (m * Mutex) Lock () {/ / Fast path: grab unlocked mutex. If atomic.CompareAndSwapInt32 (& m.state, 0, mutexLocked) {if race.Enabled {race.Acquire (unsafe.Pointer (m))} return} / / Slow path (outlined so that the fast path can be inlined) m.lockSlow ()}
In atomic.CompareAndSwapInt32 (& m.state, 0, mutexLocked), m.state represents the state of the lock. Through the CAS method, we can determine whether the state of the lock is idle (m.state==0). If yes, lock it (the value of the mutexLocked constant is 1).
Atomic.Value guarantees the read and write security of any value.
The atomic package provides a set of methods at the beginning of Store to ensure the security of concurrent writes of various types of variables and to prevent other operations from reading dirty data in the process of modifying variables.
Func StoreInt32 (addr * int32, val int32) func StoreInt64 (addr * int64, val int64) func StorePointer (addr * unsafe.Pointer, val unsafe.Pointer)...
The definitions of these operating methods are similar to those described above, and I will not demonstrate how to use them.
It is worth mentioning that if you want to concurrently safely set multiple fields of a structure, in addition to converting the structure into pointers through StorePointer settings, you can also use atomic.Value introduced later in the atomic package, which completes the conversion from specific pointer types to unsafe.Pointer for us at the bottom.
With atomic.Value, it allows us not to rely on unsafe.Pointer types that do not guarantee compatibility, while encapsulating read and write operations of any data type into atomic operations (the intermediate state is not visible to the outside).
The atomic.Value type exposes two methods:
V.Store (c)-A write operation that stores the original variable c in a v of type atomic.Value.
C: = v.Load ()-read operation to read the contents stored in the previous step from thread-safe v.
I think Swap and CompareAndSwap methods have also been added in version 1.17.
The simple interface makes it easy to use, as long as Load () and Store () are used instead of variable reading and assignment operations that require concurrency protection.
Since Load () returns an interface {} type, we remember to convert it to a value of a specific type before using it. Here is a simple
The example demonstrates the use of atomic.Value.
Type Rectangle struct {length int width int} var rect atomic.Valuefunc update (width, length int) {rectLocal: = new (Rectangle) rectLocal.width = width rectLocal.length = length rect.Store (rectLocal)} func main () {wg: = sync.WaitGroup {} wg.Add (10) / / 10 concurrent updates for I: = 0; I < 10 Fmt.Printf + {go func () {defer wg.Done () update (I, iTun5)} ()} wg.Wait () _ r: = rect.Load (). (* Rectangle) fmt.Printf ("rect.width=%d\ nrect.length=%d\ n", _ r.width, _ r.length)}
You can also try assigning values to pointer variables of type Rectange without atomic.Value to see if the values of the two fields change to 10 and 15 as expected under concurrent conditions.
These are all the contents of the article "what atomic operations does Golang provide?" Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.