Comments 9
Годная статья.
Какой ад, хорошо что на практике об это не нужно думать
Думать может и не надо, но пользоваться инструментом всегда проще понимая как он работает.
И, да, это еще не весь ад, весь ад если подтянуть реализацию в много ядерном процессоре и его кешах. Вот там адище еще тот....
В зависимости от практики, конечно, но в широком смысле в материале описан подход к линеаризации процесса. Актуален даже при построении workflow
мышкой. В go
есть реализация в виде sync.RWMutex
, можно поделать весёлые `livelock`и, чтобы проникнуться статьей.
На практике оно может и не надо, но у скуфа-сеньора теперь есть +1 способ завалить вас на собесе. Хотя у него их и так полно
Кажется, явтор не понимает, что есть три явления, и они все разные:
При параллельном исполнении нескольких нитей, оперирующих общими данными, трудно угадать, как между собой будут упорядочены действия, осуществляемые разными нитями
Даже в пределах одного потока исполнения (одной нити), компилятор может изменить последовательность действий, если это ему так удобно и если это не меняет семантики кода. При этом для данной конкретной нити ничего не изменится, но если нить имеет доступ к памяти, к которой имеет доступ другие нити, то "глядя со стороны", порядок действий с памятью может измениться.
При исполнении последовательного кода процессор может переупорядочивать инструкции или операции обращения к памяти таким образом, что с точки зрения самого этого последовательного кода ничего не меняется, но глядя с другого ядра (а не нити, как в предыдущем случае) можно увидеть другой порядок обращения к памяти
Чтобы со всем этим можно было жить, существуют примитивы синхронизации, и они разные для разных случаев.
Барьеры памяти - это примитивы синхронизации между ядрами процессора (но разумеется, и компилятор не переставляет последовательные действия мимо барьера).
По поводу трех описанных явлений, мне сложно что-то ответить, поскольку они и описаны в статье.
Чтобы со всем этим можно было жить, существуют примитивы синхронизации, и они разные для разных случаев.
Вот я и постарался понять а такие ли примитивы синхронизации разные. Об этом и статья. Если Вы укажите по вашему списку какие примитивы синхронизации для какого явления стоит использовать я был бы благодарен. Обязательно посмотрю их реализацию и дополню статью.
Барьеры памяти - это примитивы синхронизации между ядрами процессора (но разумеется, и компилятор не переставляет последовательные действия мимо барьера).
Во время изучения темы я узнал про StoreBuffer. Когда поток изменяет данные, они не кладутся в кеш на прямую. А кладутся в некий буфер. Этот буфер не является общим, т.е. он принадлежит только этому потоку. Чтобы два потока могли синхронизироваться, эти данные нужно сбросить в общий ресурс. Тут на помощь приходят барьеры памяти. Которые и выполняют эту роль, именно поэтому они и наблюдаются в ассемблерных инструкциях примитивов синхронизации. Но возможно мои источники недостоверны, буду рад если поправите.
Было очень интересно узнать о том, как все это устроено в Go. Спасибо за понятную статью!
Барьеры памяти «Golang»