Mutex используется правильно? - PullRequest
0 голосов
/ 24 августа 2018

Я немного озадачен блокировкой / разблокировкой мьютекса больше раз за другим.Я использую RWMutex , и все программы будут иметь одинаковый мьютекс.

Этот код по-прежнему защищен от гонки при частом использовании мьютексов?

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
    r.Mu().RLock()
    size := len(r.redisDbs) // A
    r.Mu().RUnlock()
    if size >= int(dbId) { // B
        r.Mu().RLock()
        db := r.redisDbs[dbId] // C
        r.Mu().RUnlock()
        if db != nil { // D
            return db
        }
    }
    // E     create db...
}

Пример ситуации, о которой я думаю, может произойти:

  1. gorountine1 и goroutine2 выполняют обе эти функции
  2. оба находятся в точке A, так что переменная size равна 3
  3. условие B равно true для обеих программ
  4. оба считывают C одновременно
  5. переменная db равна нулю для обеих программ, поэтому условие C равно false
  6. теперь обе программы собираются в E и создают одну и ту же базу данных 2 раза, это плохо

Или мне нужно блокировать / разблокировать все один раз в этой ситуации?

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
    r.Mu().Lock()
    defer r.Mu().Unlock()
    size := len(r.redisDbs)
    if size >= int(dbId) {
        db := r.redisDbs[dbId]
        if db != nil {
            return db
        }
    }
    // create db...
}

Решение

func (r *Redis) RedisDb(dbId DatabaseId) *RedisDb {
    getDb := func() *RedisDb { // returns nil if db not exists
        if len(r.redisDbs) >= int(dbId) {
            db := r.redisDbs[dbId]
            if db != nil {
                return db
            }
        }
        return nil
    }

    r.Mu().RLock()
    db := getDb()
    r.Mu().RUnlock()
    if db != nil {
        return db
    }

    // create db
    r.Mu().Lock()
    defer r.Mu().Unlock()
    // check if db does not exists again since
    // multiple "mutex readers" can come to this point
    db = getDb()
    if db != nil {
        return db
    }
    // now really create it
    // ...
}

1 Ответ

0 голосов
/ 24 августа 2018

Добро пожаловать в мир синхронизации.Ваша оценка верна, есть вероятность возникновения проблем параллелизма с первой реализацией.Со вторым эти проблемы параллелизма устраняются, но полностью блокируются, и нет возможности даже одновременного доступа для чтения.У вас нет , чтобы сделать это таким образом, вы можете выполнить начальную проверку с блокировкой чтения, затем, если эта проверка определяет, что создание необходимо, установите блокировку записи, затем перепроверьте, создайте, если все ещенеобходимо, затем разблокировать.Это не необычная конструкция.Он менее эффективен (из-за выполнения проверки дважды), поэтому вы должны принять решение о компромиссе, в основном исходя из того, насколько дорогой будет выполнять проверку дважды, и как часто функция сможет работать впуть только для чтения.

...