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

Например, рассмотрим следующий фрагмент кода Kotlin с использованием сопрограмм Kotlin:

val m = mutableMapOf<Int,String>()
m[1] = "one" // (1)
yield() // or other suspending function
m[2] = "two" // (2)

У нас есть изменяемая карта m, которая мутирует дважды, и вызов функции приостановки между мутациями. Сопрограмма, выполняющая этот код, будет приостановлена ​​после строки (1) и может быть возобновлена ​​в другом потоке для выполнения строки (2). Мы никак не синхронизировали нашу изменяемую карту, поэтому теперь одна и та же карта используется двумя разными потоками. Мы обречены?

Нет. Фактически, реализация функции приостановки yield выполняет синхронизацию для нас, чтобы гарантировать, что код до yield происходит до кода после yield, чтобы он не был параллельным (т. е. последовательно).

Так что же такое одновременный? У этого слова есть точное определение. Две операции являются параллельными, если они не упорядочены отношением происходит до. Когда мы выполняем код в двух разных потоках, не имеет значения, сколько фактического времени проходит между двумя операциями. Параллелизм не определяется временем настенных часов. У нас могут быть операции (1) и (2), выполняющиеся с интервалом в десять секунд в разных потоках, и они все равно будут выполняться одновременно и вызывать у нас все виды ошибок, если между ними не существует связи произошло до.

Но откуда берется отношение происходит до? Что ж, когда мы пишем однопоточный код, все действия в потоке выполняются в программном порядке, поэтому все действия в данном потоке упорядочены, то есть последовательно . Изменять из одного потока всегда безопасно. Никакие два действия в одном потоке не могут быть одновременными.

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

Несмотря на то, что сопрограмма в Kotlin может выполняться в нескольких потоках, она похожа на поток с точки зрения изменяемого состояния. Никакие два действия в одной сопрограмме не могут быть одновременными. Как и в случае с потоками, вам следует избегать совместного использования изменяемого состояния между сопрограммами, иначе вам придется самому беспокоиться о синхронизации.

Избегайте совместного использования изменяемого состояния. Ограничьте каждый изменяемый объект одним потоком или одной сопрограммой и хорошо спите.

дальнейшее чтение

Формальное определение отношения происходит до для мира JVM можно найти в Главе 17 JLS. Эта концепция была введена Лесли Лэмпортом в его статье 1978 года Время, часы и порядок событий в распределенной системе, и она стала стандартной формальной структурой для обсуждения параллелизма во всех видах программных систем. .