2015-10-22 3 views
4

Пока я смотрел документ модели памяти golang (link), я обнаружил странное поведение на go lang. В этом документе говорится, что ниже код может случиться, что g печатает 2, а затем 0.Неправильная синхронизация в go lang

var a, b int 

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

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

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

Это только обычная проблема? Потому что мне любопытно, почему значение присваивания переменной «b» может происходить раньше, чем значение «a»? Даже если присвоение значений «a» и «b» будет происходить в другом потоке (не в основном потоке), нужно ли гарантировать, что «a» должен быть назначен до «b» в собственном потоке? (Поскольку присвоение ' a 'на первом месте, а «b» - позже). Может ли кто-нибудь рассказать мне об этой проблеме?

+0

Как я понимаю: видимость значения b в g не определяется f. _Inside_ f a задается первым, а затем b и никогда наоборот. – Volker

ответ

7

Переменные a и b выделены и инициализируется с нулевыми значениями их соответствующего типа (что 0 в случае int) прежде, чем любой из функций запуска на выполнение, по этой линии:

var a, b int 

Что может измениться - порядок новых значений присваивается им в функции f().

Цитирование с этой страницы: Happens Before:

В одном goroutine, чтения и записи должны вести себя так, как будто они выполняются в порядке, указанном в программе. То есть, компиляторы и процессоры могут изменять порядок чтения и записи, выполненных в одном канате, только если переупорядочение не изменяет поведение внутри этого goroutine, как определено спецификацией языка. Из-за этого переупорядочения порядок исполнения, наблюдаемый одной горутой, может отличаться от порядка, воспринимаемого другим. Например, если один goroutine выполняет a = 1; b = 2;, другой может наблюдать обновленное значение b перед обновленным значением a.

Присвоение a и b может произойти не в порядке их написания, если переназначения их не делает разницы в том же goroutine. Компилятор может их переупорядочить, например, если первое изменение значения b более эффективно (например, поскольку его адрес уже загружен в регистр). Если изменение порядка присваивания (или может) вызвать проблему в одном и том же goroutine, то, очевидно, компилятору не разрешено изменять порядок. Поскольку gotoutine функции f() ничего не делает с переменными a и b после назначения, компилятор может выполнять задания в любом порядке.

Поскольку в приведенном выше примере синхронизации между двумя goroutines нет, компилятор не прилагает никаких усилий для проверки того, что переупорядочение вызовет проблемы в другой goroutine. Это не обязательно.

Buf, если вы синхронизируете свои goroutines, компилятор удостоверится, что в «точке синхронизации» не будет никаких несоответствий: у вас будет гарантия, что в этот момент оба назначения будут «завершены»; поэтому, если «точка синхронизации» находится перед вызовами print(), тогда вы увидите напечатанные присвоенные новые значения: 2 и 1.

+0

Спасибо icza. Хм .. это компилятор и процессор. (Я имею в виду, что это не проблема, но причиной является компилятор и процессор.) –

+1

@KyuntaeEthanKim Это оптимизация _and_ проблема синхронизации: ваши goroutines не синхронизированы, поэтому у вас нет гарантии, что и если какое-либо из обновленных значений будет наблюдаться другим goroutine. Он может печатать '2' и' 1', он может печатать '2' и' 0', он может печатать '0' и' 1', и он может печатать '0' и' 0'. – icza

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