Что такое потокобезопасность C#
Что такое потокобезопасность? Как потоки могут влиять друг на друга таким образом, что они сломаются? Как обезопасить себя от подобных поломок?
Ответы (1 шт):
Вот пример.
static void Main(string[] args)
{
int count = 0;
var task1 = Task.Run(() => {
for (int i = 0; i < 500; i++)
{
Thread.Sleep(1);
count++;
}
});
var task2 = Task.Run(() => {
for (int i = 0; i < 500; i++)
{
Thread.Sleep(1);
count++;
}
});
Task.WaitAll(task1, task2);
Console.WriteLine(count);
Console.ReadKey();
}
Какой вывод в консоль ожидается? 1000, верно?
Запускаю
1000
Вроде все ок, ну-ка еще разок
998
Опа. Вот потоки и поругались, а в чем собственно дело.
Непредсказуемость результата выполнения многопоточного кода означает, что код не является потокобезопасным.
А в том что count++ это на самом деле count = count + 1, то есть процессор должен сначала прочитать значение переменной, затем прибавить к нему единицу, затем записать в эту переменную обратно. То есть не одно действие, а несколько.
Представьте, что действия двумя потоками выполняются во времени в следующем порядке.
- Поток 1 читает значение 10 и прибавляет к нему 1, получает 11
- Поток 2 читает значение 10 и прибавляет к нему 1, получает 11
- Поток 1 записывает значение 11 в переменную
- Поток 2 записывает значение 11 в переменную
В итоге единицу добавляли 2 раза, должно получиться 12, а получается 11.
Исправить просто - использовать потокобезопасный Increment.
static void Main(string[] args)
{
int count = 0;
var task1 = Task.Run(() => {
for (int i = 0; i < 500; i++)
{
Thread.Sleep(1);
Interlocked.Increment(ref count);
}
});
var task2 = Task.Run(() => {
for (int i = 0; i < 500; i++)
{
Thread.Sleep(1);
Interlocked.Increment(ref count);
}
});
Task.WaitAll(task1, task2);
Console.WriteLine(count);
Console.ReadKey();
}
Вывод
1000
И 1000 всегда.
Для более сложных операций есть так называемые примитивы синхронизации, например lock, когда вам надо, чтобы определенный участок кода выполнялся одновременно только во одном потоке, а остальные потоки ждали. И много других примитивов, чтобы управлять потокобезопасностью. Я лишь показал наглядный пример. У синхронизации потоков при обеспечении потокобезопасности есть новый подводный камень, который вас ждет, и называется он дедлок (deadlock). Но не спешите постить новый вопрос про дедлоки, материалов в сети на эту тему полно.