Что такое "поточно-безопасный тип"? Когда нам нужно использовать инструкцию "lock"?

Я прочитал всю документацию о потокобезопасных типах и инструкции "lock", но я до сих пор не получаю ее на 100%.

Когда мне нужно использовать инструкцию "lock"? Как это относится к (не) потокобезопасным типам? Спасибо.

Ответ 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 говорит, что вы закодированы для сценария и не получите коррупцию, если два потока обращаются к общему состоянию в одно и то же время, в то время как неизменность просто говорит, что вы возвращаете новый объект, а не изменяете его. Неизменяемые объекты являются потокобезопасными, но не все поточно-безопасные объекты неизменяемы.

Ответ 2

Потоковый безопасный код означает код, к которому можно получить доступ со многими потоками и по-прежнему работать правильно.

В С# это обычно требует своего рода механизма синхронизации. Простым является оператор lock (который за кулисами вызывает вызов Monitor.Enter). Кодовый блок, окруженный блоком lock, может быть доступен только по одному потоку за раз.

Любое использование типа, не являющегося потокобезопасным, требует, чтобы вы сами управляли синхронизацией.


Хороший ресурс, чтобы узнать о потоке в С#, - это бесплатная книга Джо Альбахари, найденная здесь.