Состояние гонки с мьютексом в go и куда встраивать блокировку, родительскую структуру или дочернюю структуру? - PullRequest
1 голос
/ 18 июня 2019
  1. Я вижу, как некоторые люди используют мьютексы в одной структуре.Это необходимо?Или мы можем просто поставить один единственный замок там?пример:
type Session struct {
    // some other irrelevant code
    pingLock sync.Mutex
    // some other irrelevant code
    streamLock sync.Mutex
    // some other irrelevant code
    shutdownLock sync.Mutex
}
Если структура содержит другую структуру, а дочерняя структура содержит карту или фрагмент, куда я должен поместить мьютекс, родительскую структуру или дочернюю структуру? Почему mutex.Lock ()не работает в моем коде?Каждый раз, когда я запускаю гоночный тест, он показывает, что есть гонки.

Сначала я пытался решить первые две проблемы, поэтому я написал демо-код для его тестирования.Я пытался использовать блокировку внутри дочерней структуры, а затем родительскую структуру для блокировки состояния, но ни одна из них не сработала.Гоночный тест продолжает говорить, что есть состояние гонки.Я пробовал оба t.Lock() и t.data.Lock().

type Test struct {
    name string
    data Data
    sync.RWMutex //Should I put it here?
}

type Data struct {
    d map[string]int
    sync.RWMutex // Should I put it here?
}

func (t *Test) add(key string) {
    t.data.Lock()
    defer t.data.Unlock()
    t.data.d[key] += 1
}

func (t *Test) read() {
    for {
        t.data.Lock()
        _= t.data.d["test"]
        t.data.Unlock()
    }
}

func main() {

    t := &Test{}
    t.name = "oops"
    t.data = Data{}
    t.data.d = make(map[string]int)
    t.data.d["test"] = 1

    for i := 0; i <= 10; i++ {
        go func(t *Test) {
            t.add("test")
        }(t)
        go func(t *Test) {
            t.read()
        }(t)
    }
    time.Sleep(time.Second * 3)
    fmt.Printf("result is %v", t.data.d["test"])

Ответы [ 2 ]

0 голосов
/ 19 июня 2019

Как уже упоминалось @peterSO, ошибка вызвана "fmt.Printf (" результат равен% v \ n ", t.data.d [" test "])". После нескольких часов копания я, кажется, нашел ответы на свои первые два вопроса. Я неправильно понял концепцию мьютекса. Мьютекс используется для защиты ресурса, а не для блокировки самой памяти, в моем примере, самой структуры. По первому вопросу: если одна программа выполняет некоторый код, такой как

s.pingLock.Lock()
\\ some logic 1
s.pingLock.Unlock()
streamLock
\\ some logic 2
streamUnlock

Таким образом, когда одна подпрограмма выполняет этот код и получает s.streamLock.Lock(), пока она не будет разблокирована, другие подпрограммы go не могут выполнить some logic 2, но любая подпрограмма может выполнить some logic1, если она получает pingLock.Lock(). Если используется только одна блокировка, если одна подпрограмма go получает блокировку, никто другой не может получить блокировку, тогда все другие выполнения блокируются.

По второму вопросу: Если понимание объяснения выше. Тогда ответ на второй вопрос тоже. Размещайте mutex где угодно, потому что это просто блокировка для защиты ресурса, то есть самого кода. Я уверен, что есть хороший идиоматический способ сделать это.

Я не уверен, правильно ли так поступать. Если у кого-то другое мнение или лучший ответ, пожалуйста, дайте мне знать.

0 голосов
/ 18 июня 2019

В гоночном тесте все время говорят, что есть состояние гонки.


Заявление

fmt.Printf("result is %v", t.data.d["test"])

передает аргумент

t.data.d["test"]

по значению. Он делает копию по назначению, что является чтением.

Вам нужно защитить чтение через мьютекс.

t.data.Lock()
fmt.Printf("result is %v\n", t.data.d["test"])
t.data.Unlock()
...