2016-04-11 3 views
4

Мне нужно read preferring RW mutex в golang. Есть ли пакет в голанге, который удовлетворит мои потребности. Я попробовал sync.RWMutex, но, похоже, он предпочитает блокировку записи. Вот идет моя попытка выделить RWMutex Гоу,Читайте предпочитаете блокировку мьютекса RW в Голанге

package main 

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

func main() { 

y := &resource{x: 10} 

go func() { 
    defer fmt.Println("done first read") 
    y.RLock() 
    defer y.RUnlock() 
    go func() { 
     defer fmt.Println("done first write") 
     fmt.Println("first write req") 
     y.Lock() 
     fmt.Println("after first write granted") 
     defer y.Unlock() 
    }() 
    time.Sleep(time.Second) 
    go func() { 
     defer fmt.Println("done second read") 
     fmt.Println("second read req") 
     y.RLock() 
     fmt.Println("after second read granted") 
     defer y.RUnlock() 
    }() 

    time.Sleep(10 * time.Second) 
}() 

time.Sleep(time.Minute) 

} 

type resource struct { 
    sync.RWMutex 
    x int 
} 

Выход:

first write req 
second read req 
done first read 
after first write granted 
done first write 
after second read granted 
done second read 

Второй читатель остается ждать, пока автор снимает блокировку.

ответ

4

sync.RWMutex выполняет как предпочтительную запись, так и предпочитаемую запись. Все зависит от того, как вы используете его для получения предпочтительной записи или чтения.

Берет википедию код ссылки псевда как пример блокировки-For-Read (в ситуации чтения предпочтительного):

* Input: mutex m, condition variable c, integer r (number of readers waiting), flag w (writer waiting). 
* Lock m (blocking). 
* While w: 
* wait c, m[a] 
* Increment r. 
* Unlock m. 

и шаблон блокировка для-записи в sistuation чтения предпочтительного в качестве пока вы по образцу выше для блокировки-For-Читает:

* Lock m (blocking). 
* While (w or r > 0): 
* wait c, m 
* Set w to true. 
* Unlock m. 

вы можете увидеть этот механизм в игре в течение как RWMutex реализуется. Помните, что рамки Go просто Go код - просмотреть код, чтобы увидеть, как это реализовано:

https://golang.org/src/sync/rwmutex.go?s=879:905#L20

29 // RLock locks rw for reading. 
30 func (rw *RWMutex) RLock() { 
31  if race.Enabled { 
32   _ = rw.w.state 
33   race.Disable() 
34  } 
35  if atomic.AddInt32(&rw.readerCount, 1) < 0 { 
36   // A writer is pending, wait for it. 
37   runtime_Semacquire(&rw.readerSem) 
38  } 
39  if race.Enabled { 
40   race.Enable() 
41   race.Acquire(unsafe.Pointer(&rw.readerSem)) 
42  } 
43 } 

Ключ отметить, в приведенном выше коде, который дает вам ваш integer r в rw.readerSem википедия пример картины, какие языки (например, Go и другие) называют Семафор:

http://www.golangpatterns.info/concurrency/semaphores

реальное мясо от ожидания на линии 37, для runtime_Semaquire():

https://golang.org/src/sync/runtime.go

11 // Semacquire waits until *s > 0 and then atomically decrements it. 
12 // It is intended as a simple sleep primitive for use by the synchronization 
13 // library and should not be used directly. 
14 func runtime_Semacquire(s *uint32) 

Зная это, и видя, как Приращивает RWMutex.RLock() чтения, что число, вы можете реорганизовывать свой код соответственно.

Взгляните на то, как RWMutex.RUnlock декрементах что, но самое главное, как RWMutex.Lock() заставляет ждать всех активных читателей:

71 // Lock locks rw for writing. 
72 // If the lock is already locked for reading or writing, 
73 // Lock blocks until the lock is available. 
74 // To ensure that the lock eventually becomes available, 
75 // a blocked Lock call excludes new readers from acquiring 
76 // the lock. 
77 func (rw *RWMutex) Lock() { 
78  if race.Enabled { 
79   _ = rw.w.state 
80   race.Disable() 
81  } 
82  // First, resolve competition with other writers. 
83  rw.w.Lock() 
84  // Announce to readers there is a pending writer. 
85  r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders 
86  // Wait for active readers. 
87  if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { 
88   runtime_Semacquire(&rw.writerSem) 
89  } 
90  if race.Enabled { 
91   race.Enable() 
92   race.Acquire(unsafe.Pointer(&rw.readerSem)) 
93   race.Acquire(unsafe.Pointer(&rw.writerSem)) 
94  } 
95 } 

Это, скорее всего, почему вы видите 2-го чтения ожидания.

Помните, что семафоры делятся не только с созданным вами экземпляром RWMutex, но и на протяжении всего времени выполнения, чтобы спланировать другие горуты и другие блокировки. Следовательно, почему попытка заставить шаблон может принести больше вреда, чем пользы в целом в приложении.

Моя рекомендация - сделать шаг назад и рассмотреть, почему вы предпочитаете блокировку в вашей архитектуре вообще. Действительно ли вы на уровне производительности, что переключение контекста процессора замедляет ваше высокочастотное приложение?Я бы сказал, что есть более систематический подход, который можно было бы предпринять, вместо того, чтобы пытаться реализовать шаблон «блокировки чтения с привилегией» только потому, что он звучит круто и звучит так, будто он решает все ваши проблемы. Каковы ваши контрольные показатели? Каков размер входных данных и сколько параллельных процессов? Нужно ли делиться этим? Является ли это потреблением памяти в ХБ ГБ и вы можете переключиться на то, чтобы помещать вещи в стек (например, каналы, без блокировки мьютекса)? Как насчет прочитанных данных в стеке и сохранить набор записи для блокировки? Как долго до тех пор, пока GC не очистит стек, а не будет держать вещи в куче? Etc и т.д.

3

Кажется, вы можете достичь желаемого поведения с sync.WaitGroupsync primitive например

var wg sync.WaitGroup 
go func() { 
      defer fmt.Println("done second read") 
      fmt.Println("second read req") 
      y.RLock() //wait writer 
      wg.Add(1) //report busy 
      fmt.Println("after second read granted") 
      defer wg.Done() //report done 
      defer y.RUnlock() 
     }() 
//forcing writer to wait all readers 
go func() { 
      defer fmt.Println("done first write") 
      fmt.Println("first write req") 
      wg.Wait() //wait all readers 
      y.Lock() 
      fmt.Println("after first write granted") 
      defer y.Unlock() 
     }() 

Вы можете попробовать https://play.golang.org/p/y831xIrglj

+0

Ницца. Я не использую мьютекс много и просто дал длинный разлив на том, как его можно исследовать. – eduncan911

 Смежные вопросы

  • Нет связанных вопросов^_^