Назначение «родных» типов данных (32 бита) является атомарным на большинстве платформ (включая x86). Это означает, что назначение произойдет полностью, и вы не рискуете иметь «наполовину обновленную» переменную dat. Но это единственная гарантия, которую вы получаете.
Я не уверен насчет присвоения двойного типа данных. Вы можете посмотреть его в спецификации x86 или проверить, дает ли .NET какие-либо явные гарантии. Но в общем, типы данных, которые не имеют «родного размера», не будут атомарными. Даже более мелкие из них, например bool, могут и не быть (поскольку для записи bool может потребоваться прочитать все 32-разрядное слово, перезаписать один байт, а затем снова записать все 32-разрядное слово)
Как правило, резьба может прерываться между любыми двумя инструкциями по сборке.
Это означает, что ваш приведенный выше код является поточно-ориентированным, если вы не пытаетесь читать из dat (что, как вы можете утверждать, делает его довольно бесполезным).
Атомность и безопасность потоков - это не одно и то же. Безопасность потока полностью зависит от контекста. Ваше присвоение dat является атомарным, поэтому другой поток, считывающий значение dat, увидит либо старое, либо новое значение, но никогда не будет "промежуточным". Но это не делает его безопасным. Другой поток может прочитать старое значение (скажем, размер массива) и выполнить операцию на его основе. Но вы можете обновить dat сразу после того, как он прочитает старое значение, возможно, установив для него меньшее значение. Другой поток может теперь получить доступ к вашему новому, меньшему массиву, но считаю, что он имеет старый, больший размер.
i ++ и ++ i также не потокобезопасны, поскольку они состоят из нескольких операций (значение чтения, значение приращения, значение записи), и в общем случае все, что состоит из операций чтения и записи, не потокобезопасен.
Потоки также могут быть прерваны при настройке стека вызовов для вызова функции, да. После любой инструкции ассемблера.