Three steps to change a value
- Take out the value you want to modify from somewhere
- Modify the retrieved value to the expected value
- Save the modified value to the original place
question
If another process (process or thread) performs the same operation (value, modification) on the same value when both processes need to do step 3, there must be one process for nothing.
Pessimistic lock
The pessimistic lock always believes that concurrency problems will occur and belong to the conservative.
If you want to modify a value, immediately add a lock to the value to indicate that the value is being modified and no one can modify it; then start three steps, and after the three steps process is over, the lock will be unlocked.
When there are other processes that want to modify the same value, when you see the lock, you don’t go through three steps, but choose to wait; when the lock is unlocked, you also add a lock to the value, and then start three steps. After the three steps are completed, the lock will be unlocked.
Optimistic lock
Optimistic Lock always believes that concurrency problems will not occur and belong to the Optimistic School.
When modifying the data, do not add locks. Steps 1 and 2 are normally carried out. When performing step 3, confirm whether the value has been modified. If it has been modified, give up the modification and go again to steps 1, 2, and 3 (or give up modifying the value).
Optimistic locks and pessimistic locks in Go language
sync/atomic
Go language has an atomic package that can complete concurrent and safe value replacement operations without forming critical areas and creating mutexes. This package applies the principle of optimistic locking.
However, this package only supports some basic operations of int32/int64/uint32/uint64/uintptr, such as addition and deduction, exchange, loading, storage, etc.
sync
The sync package in Go language provides various locks. If this package is used, it will basically be in the working mode of pessimistic locks.
Go code example
package main import ( "fmt" "sync" "sync/atomic" "time" ) var ( x int64 mu wg ) // Normal functions, concurrency is not safefunc Add() { x++ () } // Mutex lock, concurrent security, performance lower than atomic operationfunc muAdd() { () x++ () () } // Atomic operation, concurrency security, performance is higher than mutex locks, and is only used for some basic data types in gofunc AmAdd() { atomic.AddInt64(&x, 1) () } func main() { // Atomic operation atomic package // Locking operation involves kernel-state context switching, which is time-consuming and expensive // We can also use atomic operations to ensure concurrency security for basic data types // Because atomic operations are methods provided by Go language, we can complete them in the user state, so the performance is better than locking operations // Atomic operation of the go language is completed by the built-in library, sync/atomic start := () for i := 0; i < 10000; i++ { (1) go Add() // The normal version of the Add function is not concurrency-safe // go muAdd() // The locked version of Add function is concurrently safe, but the locking performance is expensive // go AmAdd() // Atomic operation version Add function is concurrency-safe and has better performance than locked version } end := () () (x) ((start)) }
Reference Blog 1
Reference Blog 2
This is the article about the specific use of optimistic locks and pessimistic locks in Go language. For more related Go languages, please search for my previous articles or continue to browse the related articles below. I hope everyone will support me in the future!