Любая программа, в которой нет доступа к изменяемым аспектам состояния программы более чем одним потоком, будет тривиально поточно-ориентированной, так как каждый поток также может быть своей отдельной программой. Однако полезная многопоточность обычно требует взаимодействия между потоками, что подразумевает существование некоторого изменяемого общего состояния.
Ключом к безопасному и эффективному многопоточности является включение изменчивости на правильном «уровне проектирования». В идеале каждый аспект состояния программы должен быть представлен одной неизменяемой (*), изменяемой ссылкой на объект, наблюдаемое состояние которого является неизменным. Только один поток за раз может попытаться изменить состояние, представленное определенной изменяемой ссылкой. Эффективная многопоточность требует, чтобы «изменяемый слой» в состоянии программы был достаточно низким, чтобы разные потоки могли использовать разные его части. Например, если один имеет неизменную структуру данных AllCustomers
и два потока одновременно пытаются изменить разных клиентов, каждый из них будет генерировать версию структуры данных AllCustomers
, которая включает в себя свои собственные изменения, но не версию другого потока. Не хорошо. Однако, если бы AllCustomers
был изменяемым массивом объектов CustomerState
, один поток мог бы работать на AllCustomers[4]
, в то время как другой работал на AllCustomers[9]
, без помех.
(*) Корневой путь должен существовать, когда аспект состояния становится релевантным, и не должен изменяться, когда доступ актуален. Например, можно создать AddOnlyList<thing>
, который содержит thing[][]
с именем Arr
, который был инициализирован до размера 32. Когда добавляется первое, Arr[0]
будет инициализироваться, используя CompareExchange
, в массив 16 thing
. Следующие 15 вещей будут идти в этом массиве. При добавлении 17-й вещи Arr[1]
будет инициализирован с использованием CompareExchange
для массива размером 32 (который будет содержать новый элемент и 31 элемент после него). Когда будет добавлена 49-я вещь, Arr[2]
будет инициализирован для 64 предметов. Обратите внимание, что хотя thing
сам по себе и содержащиеся в нем массивы не будут полностью неизменными, только самый первый доступ к любому элементу будет записью, и как только Arr[x][y]
будет содержать ссылку на что-либо, он будет продолжать делать это до тех пор, пока как Arr
существует.