Синхронизация операций в .NET на примерах

Всем привет. Сегодня я расскажу об инструментах, которые существуют в .NET для параллельной работы с какими-то внешними ресурсами и приведу примеры, где и как их можно применить.

При параллельной работе с каким-то ресурсом, нам нужно синхронизировать доступ к нему, чтобы не попасть в состояние гонки или банально не перегрузить его. Для этого в .NET существуют следующие вещи:

  1. lock-object. Это самый простой способ синхронизации. Мы заводим объект, который будем использовать для блокировки параллельного выполнения какого-то участка кода.

    1. Применять стоит, когда нам нужно, чтобы какой-то участок кода в один момент времени выполнялся только одним потоком. Тут может быть любая работа с файлами, БД и другими ресурсами.

    2. Важно понимать, что lock работает на уровне приложения, а не ОС, поэтому другое приложение может спокойно занять наш ресурс.

    3. Еще следует помнить, что код в lock секции должен выполняться в рамках одного потока, поэтому мы не можем использовать внутри асинхронные вызовы.

      Синтаксис

      Синтаксис

  2. Mutex. Используется для ограничения доступа к ресурсу на уровне ОС.

    1. Его может освободить только тот поток, который его занял.

    2. Подойдет для ограничения доступа к файлам.

      Синтаксис

      Синтаксис

  3. SemaphoreSlim. Облегченная версия семафора. Сам семафор предоставляется ОС и используется для того, чтобы ограничить число одновременных пользователей ресурса.

    1. Работает на уровне ОС.

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

    3. Слим версия может быть асинхронной, что полезно для работы с файлами, к которым мы хотим ограничить доступ. На работе некоторые настройки сервисов мы храним в .json-файлах, для ограничения доступа к ним из нескольких потоков, мы используем слим версию и асинхронное ожидание.

      Синтаксис

      Синтаксис

  4. AutoResetEvent. Как и классы выше служит для синхронизации доступа к ресурсу.

    1. Отличие в том, что позволяет управлять одним потоком из другого.

      Синтаксис

      Синтаксис

  5. Interlcoked. Служит для произведения атомарных операций.

    1. Подходит, если есть какая-то переменная, которую мы хотим атомарно изменять.

    2. Еще с его помощью можно поставить флаг на какую-то часть кода, которую должен выполнять какой-то из потоков, но нам не важно какой. Тогда при первом входе в метод поток будет поднимать флаг через Interlocked, а другие потоки будут выходить из метода, когда будут видеть, что флаг уже поднят.

      Пример работы с флагом через Interlocked

      Пример работы с флагом через Interlocked

Это те вещи, которые я часто встречаю во время работы, и которые могут серьезно облегчить жизнь, если мы собираемся работать с каким-то ресурсом параллельно. Надеюсь кому-то эта статья поможет узнать что-то новое или разобраться с каким-то из этих способов синхронизации.

© Habrahabr.ru