Чтобы ответить на ваше обновление, ни один код не будет потокобезопасным.Вы читаете i, увеличиваете значение, а затем пишете в i.Эти три шага не являются одной единицей логики, другие потоки могут взаимодействовать с i между шагами чтения и записи.
Например, у вас есть три потока, AB и C. Что-то происходит, что заставляет B работать медленнее, чемдругие потоки.
A: Read i to thread local memory location A the value 10
B: Read i to thread local memory location B the value 10
A: Add 1 to thread local memory location A
A: Write 11 to i from thread local memory location A
B: Add 1 to thread local memory location B
C: Read i to thread local memory location C the value 11
C: Add 1 to thread local memory location C
C: Write 12 to i from thread local memory location C
B: Write 11 to i from thread local memory location B
Поскольку 3 операции, не являющиеся «атомарными», 3 потока могут выполнять работу между 3 шагами, которые должен был выполнить B, это приводит к неверному конечному значению.
Обычный способ справиться с этим - либо заблокировать 3 операции, чтобы только один поток мог сделать это одновременно,
lock(someObject)
{
i = i + 1;
}
использовать инструмент, который делает операцию атомарной
Interlocked.Increment(ref i);
или убедитесь, что значение i не изменилось между началом чтения и записью, которую вы хотите выполнить, и, если она изменилась, повторите операцию.
int iOriginal, iNew;
do
{
iOriginal = i;
iNew = iOriginal + 1;
} while(iOriginal != Interlocked.CompareExchange(ref i, iNew, iOriginal)
Причина, по которой люди говорят, что неизменяемые значенияявляются потокобезопасными, если они ссылаются на передачу копии ссылки на другую функцию, после создания копии этой ссылки вам не нужно беспокоиться оПоток изменяет значение этого объекта, пока вы с ним работаете.Но если вы не делаете копию ссылки (как в вашем примере, где вы использовали разделяемую переменную вне области действия функции), вы сталкиваетесь с проблемами неизменности ссылки, которую вы используете между потоками.
Проще говоря, значение 10 является неизменным, переменная с именем i
- нет.Если вы разделяете переменную (я говорю не об отношении объекта / значении, которое имеет переменная, а о самой переменной) между потоками, то вы работаете с изменяемым объектом.