Halo!
Pada artikel ini, saya ingin memberi tahu Anda bagaimana Anda bisa membuat RWMutex Anda sendiri, tetapi dengan kemampuan untuk melewati kunci dengan batas waktu atau dengan memicu konteks. Artinya, terapkan TryLock (konteks.Context) dan RTryLock (konteks.Context), tetapi untuk Mutex Anda sendiri.

Gambar menunjukkan cara menuangkan air ke leher yang sangat sempit.
Untuk memulainya, harus diklarifikasi bahwa untuk 99% tugas, metode seperti itu tidak diperlukan sama sekali. Mereka dibutuhkan ketika sumber daya yang diblokir mungkin tidak dirilis untuk waktu yang sangat lama. Saya ingin mencatat bahwa jika sumber daya yang diblokir tetap sibuk untuk waktu yang lama, Anda harus terlebih dahulu mencoba mengoptimalkan logika sedemikian rupa untuk meminimalkan waktu pemblokiran.
Untuk informasi lebih lanjut, lihat Menari dengan Mutex di Go di Contoh 2.
Namun, jika, bagaimanapun, kita harus memiliki retensi lama dari satu aliran sumber daya, maka menurut saya TryLock akan sulit dilakukan tanpanya.
, , atomic, . , . , , . , , .
Mutex:
// RWTMutex - Read Write and Try Mutex
type RWTMutex struct {
state int32
mx sync.Mutex
ch chan struct{}
}
state — mutex, atomic.AddInt32, atomic.LoadInt32 atomic.CompareAndSwapInt32
ch — , .
mx — , , .
:
// TryLock - try locks mutex with context
func (m *RWTMutex) TryLock(ctx context.Context) bool {
if atomic.CompareAndSwapInt32(&m.state, 0, -1) {
return true
}
// Slow way
return m.lockST(ctx)
}
// RTryLock - try read locks mutex with context
func (m *RWTMutex) RTryLock(ctx context.Context) bool {
k := atomic.LoadInt32(&m.state)
if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) {
return true
}
// Slow way
return m.rlockST(ctx)
}
Seperti yang Anda lihat, jika Mutex tidak dikunci, maka Mutex dapat dengan mudah diblokir, tetapi jika tidak, maka kita akan beralih ke skema yang lebih kompleks.
Pada awalnya, kami mendapatkan saluran, dan kami masuk ke loop tak terbatas, jika ternyata terkunci, kami keluar dengan sukses, dan jika tidak, maka kami mulai menunggu salah satu dari 2 acara, atau saluran tersebut tidak diblokir, atau aliran ctx.Done () akan membuka blokir:
func (m *RWTMutex) chGet() chan struct{} {
m.mx.Lock()
if m.ch == nil {
m.ch = make(chan struct{}, 1)
}
r := m.ch
m.mx.Unlock()
return r
}
func (m *RWTMutex) lockST(ctx context.Context) bool {
ch := m.chGet()
for {
if atomic.CompareAndSwapInt32(&m.state, 0, -1) {
return true
}
if ctx == nil {
return false
}
select {
case <-ch:
ch = m.chGet()
case <-ctx.Done():
return false
}
}
}
func (m *RWTMutex) rlockST(ctx context.Context) bool {
ch := m.chGet()
var k int32
for {
k = atomic.LoadInt32(&m.state)
if k >= 0 && atomic.CompareAndSwapInt32(&m.state, k, k+1) {
return true
}
if ctx == nil {
return false
}
select {
case <-ch:
ch = m.chGet()
case <-ctx.Done():
return false
}
}
}
Mari buka blokir mutex.
Kami perlu mengubah status dan, jika perlu, membuka blokir saluran.
Seperti yang saya tulis di atas, jika saluran ditutup, maka case <-ch akan melewatkan aliran eksekusi lebih jauh.
func (m *RWTMutex) chClose() {
if m.ch == nil {
return
}
var o chan struct{}
m.mx.Lock()
if m.ch != nil {
o = m.ch
m.ch = nil
}
m.mx.Unlock()
if o != nil {
close(o)
}
}
// Unlock - unlocks mutex
func (m *RWTMutex) Unlock() {
if atomic.CompareAndSwapInt32(&m.state, -1, 0) {
m.chClose()
return
}
panic("RWTMutex: Unlock fail")
}
// RUnlock - unlocks mutex
func (m *RWTMutex) RUnlock() {
i := atomic.AddInt32(&m.state, -1)
if i > 0 {
return
} else if i == 0 {
m.chClose()
return
}
panic("RWTMutex: RUnlock fail")
}
Mutex itu sendiri sudah siap, Anda perlu menulis beberapa tes dan metode standar seperti Lock () dan RLock () untuk itu
Tolok ukur di mobil saya menunjukkan kecepatan ini
BenchmarkRWTMutexTryLockUnlock-8 92154297 12.8 ns/op 0 B/op 0 allocs/op
BenchmarkRWTMutexTryRLockRUnlock-8 64337136 18.4 ns/op 0 B/op 0 allocs/op
RWMutex
BenchmarkRWMutexLockUnlock-8 44187962 25.8 ns/op 0 B/op 0 allocs/op
BenchmarkRWMutexRLockRUnlock-8 94655520 12.6 ns/op 0 B/op 0 allocs/op
Mutex
BenchmarkMutexLockUnlock-8 94345815 12.7 ns/op 0 B/op 0 allocs/op
Artinya, kecepatan kerjanya sebanding dengan RWMutex dan Mutex biasa.