Полное руководство по языку программирования С# 14 и платформе .NET 10 / Многопоточность

Мониторы

Начальный ⏱ 3 мин чтения 👁 2 🕓 обновлено 11.08.2026

Наряду с оператором lock для синхронизации потоков мы можем использовать мониторы, представленные классом System.Threading.Monitor. Для управления синхронизацией этот класс предоставляет следующие методы:

  • void Enter(object obj): получает в эксклюзивное владение объект, передаваемый в качестве параметра.

    void Enter(object obj, bool acquiredLock): дополнительно принимает второй параметра - логическое значение, которое указывает, получено ли владение над объектом из первого параметра

  • void Exit(object obj): освобождает ранее захваченный объект

  • bool IsEntered(object obj): возвращает true, если монитор захватил объект obj

  • void Pulse (object obj): уведомляет поток из очереди ожидания, что текущий поток освободил объект obj

  • void PulseAll(object obj): уведомляет все потоки из очереди ожидания, что текущий поток освободил объект obj. После чего один из потоков из очереди ожидания захватывает объект obj.

  • bool TryEnter (object obj): пытается захватить объект obj. Если владение над объектом успешно получено, то возвращается значение true

  • bool Wait (object obj): освобождает блокировку объекта и переводит поток в очередь ожидания объекта. Следующий поток в очереди готовности объекта блокирует данный объект. А все потоки, которые вызвали метод Wait, остаются в очереди ожидания, пока не получат сигнала от метода Monitor.Pulse или Monitor.PulseAll, посланного владельцем блокировки.

Стоит отметить, что фактически конструкция оператора lock инкапсулирует в себе синтаксис использования мониторов. Например, в прошлой теме для синхронизации потоков применялся оператор lock:


int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}

Фактически данный пример будет эквивалентен следующему коду:


int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}

void Print()
{
    bool acquiredLock = false;
    try
    {
        Monitor.Enter(locker, ref acquiredLock);
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
    finally
    {
        if (acquiredLock) Monitor.Exit(locker);
    }
}

Метод Monitor.Enter принимает два параметра - объект блокировки и значение типа bool, которое указывает на результат блокировки (если он равен true, то блокировка успешно выполнена). Фактически этот метод блокирует объект locker так же, как это делает оператор lock. А в блоке try...finally с помощью метода Monitor.Exit происходит освобождение объекта locker, если блокировка осуществлена успешно, и он становится доступным для других потоков.

Проверь себя

📋 Тест по теме «Мониторы» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int x = _____;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int x = 0;
_____ locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}

void Print()
{
    bool acquiredLock = false;
    try
    {
        Monitor.Enter(locker, ref acquiredLock);
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
    finally
    {
        if (acquiredLock) Monitor.Exit(locker);
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < _____; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    _____ myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}

void Print()
{
    bool acquiredLock = false;
    try
    {
        Monitor.Enter(locker, ref acquiredLock);
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
    finally
    {
        if (acquiredLock) Monitor.Exit(locker);
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread._____ = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < _____; i++)
{
    Thread myThread = new(Print);
    myThread._____ = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = _____; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}

void Print()
{
    bool acquiredLock = false;
    _____
    {
        Monitor.Enter(locker, ref acquiredLock);
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
    finally
    {
        if (acquiredLock) Monitor.Exit(locker);
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Мониторы»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Мониторы», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}


void Print()
{
    lock (locker)
    {
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Мониторы»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

int x = 0;
object locker = new();  // объект-заглушка
// запускаем пять потоков
for (int i = 1; i < 6; i++)
{
    Thread myThread = new(Print);
    myThread.Name = $"Поток {i}";
    myThread.Start();
}

void Print()
{
    bool acquiredLock = false;
    try
    {
        Monitor.Enter(locker, ref acquiredLock);
        x = 1;
        for (int i = 1; i < 6; i++)
        {
            Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
            x++;
            Thread.Sleep(100);
        }
    }
    finally
    {
        if (acquiredLock) Monitor.Exit(locker);
    }
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Мониторы»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Мониторы»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Мониторы». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.