Ответ 1
Представьте экземпляр класса с глобальной переменной. Представьте, что два потока вызывают метод на этом объекте в одно и то же время, и этот метод обновляет глобальную переменную внутри.
Вероятность того, что значение в переменной будет искажено. Различные языки и компиляторы/переводчики будут иметь дело с этим по-разному (или совсем не...), но дело в том, что вы получаете "нежелательные" и "непредсказуемые" результаты.
Теперь представьте, что метод получает "блокировку" переменной, прежде чем пытаться прочитать или записать в нее. Первый поток для вызова метода получит "блокировку" для переменной, второй поток для вызова метода должен будет ждать, пока блокировка не будет освобождена первым потоком. Хотя у вас все еще есть условие гонки (т.е. Второй поток может переписать значение из первого), по крайней мере, у вас есть предсказуемые результаты, потому что ни один из двух потоков (которые не знают друг о друге) может одновременно изменить значение.
Вы используете оператор lock
для получения этой блокировки для переменной. Обычно вы определяете отдельную переменную объекта и используете ее для объекта блокировки:
public class MyThreadSafeClass
{
private readonly object lockObject = new object();
private string mySharedString;
public void ThreadSafeMethod(string newValue)
{
lock (lockObject)
{
// Once one thread has got inside this lock statement, any others will have to wait outside for their turn...
mySharedString = newValue;
}
}
}
Тип считается "потокобезопасным", если он применяет принцип отсутствия коррупции, если доступ к общим данным осуществляется несколькими потоками одновременно.
Остерегайтесь разницы между "неизменяемыми" и "потокобезопасными". Thread-safe говорит, что вы закодированы для сценария и не получите коррупцию, если два потока обращаются к общему состоянию в одно и то же время, в то время как неизменность просто говорит, что вы возвращаете новый объект, а не изменяете его. Неизменяемые объекты являются потокобезопасными, но не все поточно-безопасные объекты неизменяемы.