Sobes.tech
Назад к вопросам
Junior — Middle
57

Можешь перечислить основные примитивы синхронизации, предоставляемые стандартной библиотекой?

Компании, где спрашивали
СБЕР ТехСБЕР Тех

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

В стандартной библиотеке C# есть несколько базовых примитивов для синхронизации потоков и координации доступа к общим данным. Чаще всего ожидают услышать про lock/Monitor, Mutex, Semaphore/SemaphoreSlim, ReaderWriterLockSlim, AutoResetEvent и ManualResetEventSlim. Важно понимать, что они решают разные задачи: взаимное исключение, ограничение числа одновременных операций, ожидание события и защиту чтения/записи.

Определение:

Примитивы синхронизации — это механизмы, которые помогают безопасно работать с разделяемыми ресурсами в многопоточном коде. Они предотвращают гонки данных, обеспечивают упорядочивание доступа и позволяют потокам ждать друг друга или внешнего события. В C# часть из них встроена в язык и платформу, часть предоставляется как классы в System.Threading.

Пример использования:

Например, если несколько потоков увеличивают общий счётчик, доступ к нему нужно защитить. Для этого обычно используют lock, а если нужно ограничить количество одновременно работающих потоков — SemaphoreSlim.

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static readonly object _sync = new object();
    private static int _counter = 0;

    static async Task Main()
    {
        var tasks = new Task[5];

        for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = Task.Run(() =>
            {
                lock (_sync)
                {
                    _counter++;
                }
            });
        }

        await Task.WhenAll(tasks);
        Console.WriteLine(_counter);
    }
}

Пояснение кода:

Код показывает защиту общего ресурса с помощью lock. Все потоки пытаются увеличить _counter, но одновременно критическую секцию может выполнить только один поток. Объект _sync используется как монитор для синхронизации, а сам инкремент вынесен внутрь защищённого блока, чтобы не возникло гонки данных.

Если бы задача была не в защите переменной, а в ограничении параллелизма, вместо lock подошёл бы SemaphoreSlim: он позволяет пропускать только заданное число потоков одновременно. Для ожидания сигнала от другого потока или события использовали бы AutoResetEvent или ManualResetEventSlim.

Ключевые моменты:

  • lock / Monitor — самый частый способ взаимного исключения для защиты критической секции.
  • Mutex подходит для синхронизации между процессами, но обычно тяжелее, чем lock.
  • Semaphore / SemaphoreSlim ограничивают количество одновременно выполняемых операций.
  • ReaderWriterLockSlim полезен, когда чтений много, а записей мало.
  • AutoResetEvent и ManualResetEventSlim нужны для координации потоков через сигналы и ожидание событий.
  • Важно выбирать примитив по задаче: защита данных, ограничение параллелизма или ожидание сигнала — это разные сценарии.