Implementing the Trylock method for Go mutexes

Source: Internet
Author: User
Tags cas high cpu usage
This is a creation in Article, where the information may have evolved or changed.

Catalogue [−]

    1. Use unsafe to manipulate pointers
    2. Implement spin Lock
    3. Using the channel to achieve
    4. Performance comparison
    5. Resources

Go standard library sync/Mutex , RWMutex implements the sync/Locker interface, provides the Lock() UnLock() method, can acquire the lock and the release lock, we can use it conveniently to control our concurrency control to the shared resource.

However, when a lock in the standard library is Mutex.Lock obtained, it will be blocked if it is not released before it is freed Lock , and this design may not meet my needs in some cases. Sometimes we want to try to get the lock, if we get it, no problem to continue, if we can't get it, we don't want to block it, but we want to call the other logic, this time we need the TryLock method.

Although it was early (13) to ask for the Go Development Group, the request was not included in the official repository and eventually closed in the official repository, which is not currently being added by the official repository.

sync/Mutexby the way, the source code implementation can be accessed here, it should be implemented a spin (spin) plus sleep way to achieve, interested readers can read the source code, or read related articles, such as Go Mutex source analysis. This is not the content of this article, readers can find some information to read.

Well, turn to the point and see how several implementations TryLock are made.

Using the unsafe action pointer

If you look at sync/Mutex the code, you will find Mutext the data structure as follows:

1234
type struct int32Sema  uint32}

It uses state this 32-bit integer to mark the lock's occupancy, so we can use it CAS to try to acquire the lock.

The code is implemented as follows:

123456789
const mutexlocked = 1iotatypestruct {sync. Mutex}funcbool {return atomic.compareandswapint32 (*int32) (unsafe. Pointer (&m.mutex)), 0, mutexlocked)}

Used in the same way as standard libraries Mutex .

 12345678910111213141516171819 
td>
 func  main () {var  m Mutexm.lock ( ) go  func  () {M.lock ()} () time. Sleep (time. Second) fmt. Printf ( "Trylock:%t\n" , M.trylock ()) //false  fmt. Printf ( "Trylock:%t\n" , M.trylock ()) //false  M.unlock () Fmt. Printf ( "Trylock:%t\n" , M.trylock ()) //true  fmt. Printf ( "Trylock:%t\n" , M.trylock ()) //false  m.unlock () Fmt. Printf ( "Trylock:%t\n" , M.trylock ()) //true  m.unlock ()} 

Note that TryLock instead of checking the status of the lock, it attempts to acquire the lock, so TryLock when it returns True, the lock is actually acquired.

Implement spin Lock

The above section gives us the inspiration to use uint32 and CAS manipulate us to a custom lock:

1234567891011121314151617
type struct UInt32} func (SL *spinlock) Lock () { for!SL. Trylock () {runtime. Gosched ()}}func (SL *spinlock) Unlock () {Atomic. StoreUint32 (&SL.F, 0)}funcbool {return atomic. CompareAndSwapUint32 (&SL.F, 0, 1)}

Overall, it seems to be a lite version of the standard library, with no sleep and wake-up features.

Of course, this spin lock can be high in the case of large concurrency, because its Lock method uses a spin, and if someone does not release the lock, the loop will be executed at a faster speed but with high CPU usage.

Of course, this version can be further optimized, especially at the time of copying. The following is an optimized version:

1234567891011121314151617181920
 type  spinLock uint32  func  (SL *spinlock) Lock () {for !atomic. CompareAndSwapUint32 ((*uint32 ) (SL),  0 ,  1 ) {runtime. Gosched () //without This it locks up on Gomaxprocs > 1 }}func  (SL *spinlock) Unlock () {Atomic. StoreUint32 ((*uint32 ) (SL),  0 )} Func  (SL *spinlock) Trylock () bool  {return  atomic. CompareAndSwapUint32 ((*uint32 ) (SL),  0 ,  1 )} func  SpinLock () sync. Locker {var  lock spinlockreturn  &lock} 

Using the channel to achieve

Another way is to use the channel:

12345678910111213141516171819202122232425
typeChanmutexChan struct{}func(M *chanmutex) Lock () {ch: = (Chan struct{}) (*M) Ch <-struct{}{}}func(M *chanmutex) Unlock () {ch: = (Chan struct{}) (*m)Select{ Case<-ch:default:Panic("Unlock of unlocked mutex")}}func(M *chanmutex) Trylock ()BOOL{ch: = (Chan struct{}) (*m)Select{ CaseCH <-struct{}{}:return truedefault:}return false}

Interested students can pay attention to the library Lrita/gosync written by my colleague.

Performance comparison

First look at the Mutex RWMutex Lock performance Comparisons in the above three ways and in the standard library Unlock :

12345
Benchmarkmutex_lockunlock-4         100000000       16.8Ns/op0B/op0Allocs/opbenchmarkrwmutex_lockunlock-4       50000000       36.8Ns/op0B/op0Allocs/opbenchmarkunsafemutex_lockunlock-4   100000000       16.8Ns/op0B/op0Allocs/opbenchmarkchannmutex_lockunlock-4    20000000       65.6Ns/op0B/op0Allocs/opbenchmarkspinlock_lockunlock-4      100000000       18.6Ns/op0B/op0Allocs/op

can see single-threaded (goroutine) in the case of ' spinlock ' is not much better than the standard library, but almost, the situation of concurrent testing is better, as shown in the table below, this is expected.

unsafeThe same way as the standard library.

channelThe performance of the method is relatively poor.

12345
Benchmarkmutex_lockunlock_c-4         20000000       75.3Ns/op0B/op0Allocs/opbenchmarkrwmutex_lockunlock_c-4       20000000       -Ns/op0B/op0Allocs/opbenchmarkunsafemutex_lockunlock_c-4   20000000       75.3Ns/op0B/op0Allocs/opbenchmarkchannmutex_lockunlock_c-4    10000000      231Ns/op0B/op0Allocs/opbenchmarkspinlock_lockunlock_c-4      50000000       32.3Ns/op0B/op0Allocs/op

Then look at the TryLock performance of the lock for three implementations:

123
Benchmarkunsafemutex_trylock-4        50000000        34.0 ns/op       0 b/op      0 Allocs/opbenchmarkchannmutex_trylock-4         20000000        83.8 ns/ Op       0 b/op       0 allocs/opbenchmarkspinlock_trylock-4           50000000        30.9 ns/op       0 b/op       0 allocs/op

Resources

This article refers to the following articles and open source projects:

    1. https://github.com/golang/go/issues/6123
    2. https://github.com/LK4D4/trylock/blob/master/ Trylock.go
    3. https://github.com/OneOfOne/go-utils/blob/master/sync/spinlock.go
    4. /http Codereview.stackexchange.com/questions/60332/is-my-spin-lock-implementation-correct
    5. https://github.com /lrita/gosync
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.