Могу ли я использовать блокировку для обеспечения порядка команд? - PullRequest
0 голосов
/ 21 февраля 2020

Как указано в этой ссылке https://golang.org/ref/mem, приведенный ниже код использует неправильную синхронизацию:

var a, b int

func f() {
    a = 1
    b = 2
}

func g() {
    print(b)
    print(a)
}

func main() {
    go f()
    g()
}

Поскольку возможно, что он печатает a = 0 и b = 2.

Однако мне интересно, возможен ли этот результат в приведенном ниже коде, где a и b защищены с помощью Lock:

var a, b int
var mu sync.Mutex

func f() {
    mu.Lock()
    a = 1
    b = 2
    mu.Unlock()
}

func g() {
    mu.Lock()
    print(b)
    print(a)
    mu.Unlock()
}

func main() {
    go f()
    g()
}

Поскольку ссылка говорит:

For any sync.Mutex or sync.RWMutex variable l and n < m, call n of l.Unlock() happens before call m of l.Lock() returns.

Однако неясно, будет ли гарантированно выполняться присваивание a и b до оператора Unlock

Ответы [ 2 ]

4 голосов
/ 21 февраля 2020

Код безопасен, поскольку в нем нет гонки данных.

Однако поведение не определено. Синхронизация с расписанием рутин не выполняется, поэтому может случиться так, что g() сначала блокирует мьютекс, и как только он освобождается, main() заканчивается, и ваше приложение может завершить его, а f() может не завершиться (может даже не запуститься ). Приложение может ничего не печатать, может печатать 2 или может печатать 21.

. Также может быть, что f() сначала блокирует, переносит назначения, а затем g() в main() распечатает присвоенные новые значения: 21.

Если вы хотите, чтобы f() назначил первым, вы можете использовать sync.WaitGroup, например:

var a, b int
var wg sync.WaitGroup

func f() {
    defer wg.Done()
    a = 1
    b = 2
}

func g() {
    print(b)
    print(a)
}

func main() {
    wg.Add(1)
    go f()
    wg.Wait()
    g()
}

. Это всегда будет печатать 21, попробуйте на игровой площадке Go .

И запутанный пример, чтобы доказать, что это можно решить также с помощью sync.Mutex. Это не предназначено для использования, но это также работает:

Блокировка mu в main() перед запуском программы. Имейте также g() блокировку, которая, очевидно, сначала заблокирует. f() может разблокировать его, как только его работа будет выполнена, давая "зеленый свет" g():

var a, b int
var mu sync.Mutex

func f() {
    a = 1
    b = 2
    mu.Unlock()
}

func g() {
    mu.Lock()
    print(b)
    print(a)
    mu.Unlock()
}

func main() {
    mu.Lock()
    go f()
    g()
}

Попробуйте это на Go Playground .

3 голосов
/ 21 февраля 2020

Гарантируется, что после выполнения f mu.Unlock() все программы, читающие a и b, увидят обновленные значения a и b, если эти программы также имеют доступ к a и * 1008. * используя тот же замок. Если есть программы, которые читают a и b без блокировки, тогда начинается гонка.

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