Как использовать RWMutex и обновить блокировки - PullRequest
0 голосов
/ 04 мая 2018

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

Я хочу убедиться, что ключ, если он существует, размещается на карте только один раз. А затем, как только ключ добавлен, читатели получают значение с карты.

Кажется, это правильно, хотя основной поток может завершиться до того, как завершатся все процедуры, поэтому я не всегда вижу напечатанный 10 Выход. Это правильный шаблон реализации?

package main

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

var downloads map[string]*sync.WaitGroup
var downloadsLock sync.RWMutex

func concurrentAccessTest() {
    // Acquire the read lock to check if the items is already there.
    downloadsLock.RLock()
    if wg, ok := downloads["item"]; ok {
        // Item exists
        downloadsLock.RUnlock()
        wg.Wait()
    } else {
        downloadsLock.RUnlock()

        fmt.Println("Upgrade Lock")
        // Item does not exist we need to add it to the map
        downloadsLock.Lock()
        fmt.Println("Writer lock obtained")
        // Check another thread hasn't gone down this path and added the item to the map
        if wg, ok := downloads["item"]; ok {
            // Item exists, no need to add it, unlock and wait
            downloadsLock.Unlock()
            wg.Wait()
            fmt.Println("Ok, now we can proceed")
        } else {
            // We are first in. Add and unlock
            wg = &sync.WaitGroup{}
            downloads["item"] = wg
            downloadsLock.Unlock()

            wg.Add(1)

            fmt.Println("Do some time consuming stuff!")
            time.Sleep(5 * time.Second)
            wg.Done()
        }
    }

    fmt.Println("Exit")
}

func main() {
    downloads = make(map[string]*sync.WaitGroup)

    // Add to the map
    for i := 0; i < 10; i++ {
        go concurrentAccessTest()
    }

    concurrentAccessTest()

    // Wait for all threads to exit
    fmt.Println("Done!")
}

1 Ответ

0 голосов
/ 04 мая 2018

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

Я думаю, что вы близки, использование RWMutex само по себе выглядит нормально, однако я считаю, что у вас есть потенциальная проблема с вызовом wg.Add(1) после снятия блокировки. Существует вероятность того, что одна из других подпрограмм прочитает WaitGroup из карты и вызовет Wait () перед выполнением вызова Add (), что, согласно документам WaitGroup, является проблемой. Документы говорят Note that calls with a positive delta that occur when the counter is zero must happen before a Wait. Вы можете легко это исправить, переместив вызов wg.Add(1) перед разблокировкой ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...