← Назад к вопросам

Когда нужно использовать Mutex?

1.8 Middle🔥 121 комментариев
#Асинхронность и многопоточность#Основы C# и .NET

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда использовать Mutex?

Mutex (сокр. от «mutual exclusion» — взаимное исключение) — это примитив синхронизации, который используется для обеспечения эксклюзивного доступа к общему ресурсу между несколькими потоками или процессами. В C# он представлен классом System.Threading.Mutex. Его ключевое отличие от других примитивов (например, lock или Monitor) — это способность работать на уровне операционной системы и синхронизировать разные процессы.

Основные сценарии использования Mutex

1. Синхронизация между процессами

Это главная причина выбрать Mutex. Если вам нужно гарантировать, что только один экземпляр приложения или определенный участок кода выполняется одновременно в разных процессах, Mutex — идеальный инструмент.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Создаём именованный Mutex, видимый всем процессам.
        // Первый аргумент — владеем ли мы им изначально (false — нет).
        // Второй аргумент — имя мьютекса в глобальном пространстве ОС.
        // Третий аргумент — был ли мьютекс создан только что.
        using (var mutex = new Mutex(false, "Global\\MyAppUniqueMutex", out bool createdNew))
        {
            if (!createdNew)
            {
                Console.WriteLine("Другой экземпляр приложения уже запущен!");
                Console.ReadKey();
                return;
            }

            Console.WriteLine("Приложение запущено. Нажмите любую клавишу для выхода.");
            Console.ReadKey();
        } // Mutex освобождается автоматически при Dispose()
    }
}

В этом примере именованный Mutex "Global\\MyAppUniqueMutex" предотвращает запуск нескольких экземпляров приложения. Если createdNew равен false, значит, мьютекс уже существует в другом процессе, и приложение завершает работу.

2. Синхронизация доступа к ресурсам, общим для нескольких процессов

Например, несколько приложений должны эксклюзивно записывать в один файл, использовать общий участок разделяемой памяти или работать с внешним устройством (например, принтером).

public class CrossProcessResourceManager
{
    private static readonly Mutex _fileMutex = new Mutex(false, "Global\\MyAppFileAccessMutex");

    public void WriteToSharedFile(string data)
    {
        _fileMutex.WaitOne(); // Захватываем мьютекс, ждём, если занят другим процессом
        try
        {
            // Критическая секция: запись в файл, доступный из разных процессов
            System.IO.File.AppendAllText("shared.log", $"{DateTime.UtcNow}: {data}\n");
        }
        finally
        {
            _fileMutex.ReleaseMutex(); // Освобождаем мьютекс
        }
    }
}

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

Mutex поддерживает рекурсивный захват — поток, который уже владеет мьютексом, может снова его захватить без взаимной блокировки (deadlock). Это полезно в сложных иерархических вызовах. Однако lock в C# тоже рекурсивен, но только в пределах одного процесса.

private static readonly Mutex _recursiveMutex = new Mutex();

public void RecursiveMethod(int depth)
{
    _recursiveMutex.WaitOne();
    try
    {
        if (depth > 0)
        {
            RecursiveMethod(depth - 1); // Рекурсивный вызов, повторный захват не блокирует поток
        }
    }
    finally
    {
        _recursiveMutex.ReleaseMutex();
    }
}

Ключевые отличия Mutex от lock (Monitor)

КритерийMutexlock (синтаксический сахар для Monitor)
Область действияМежпроцессная (можно именовать)Внутрипроцессная (только в рамках одного AppDomain)
ПроизводительностьМедленнее, т.к. работа с ядром ОСБыстрее, полностью в пользовательском режиме
РекурсивностьПоддерживаетсяПоддерживается
ИменованиеДа, позволяет глобальную синхронизациюНет

Важные особенности и лучшие практики

  1. Всегда освобождайте Mutex в блоке finally или используйте using (реализует IDisposable), чтобы избежать утечки мьютекса при исключениях.

  2. Избегайте длительного удержания Mutex. Поскольку он блокирует другие процессы, это может серьёзно сказаться на производительности системы.

  3. Именуйте осмысленно и уникально. Для глобальных мьютексов используйте префикс Global\\ (на Windows) и уникальные имена, чтобы избежать конфликтов с другим ПО.

  4. Остерегайтесь взаимных блокировок (deadlock). Тот же поток должен освобождать мьютекс, который он захватил. Не передавайте владение между потоками.

  5. Используйте WaitOne с таймаутом в сценариях, где возможна блокировка на неопределённое время.

if (mutex.WaitOne(TimeSpan.FromSeconds(5))) // Ждём не более 5 секунд
{
    try
    {
        // Критическая секция
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}
else
{
    // Таймаут: мьютекс не получен, предпринимаем альтернативные действия
}

Заключение

Mutex следует использовать в первую очередь для синхронизации между процессами. Для синхронизации потоков в пределах одного приложения в C# обычно предпочтительнее lock, Monitor, SemaphoreSlim или ReaderWriterLockSlim, так как они легче и эффективнее. Mutex — это «тяжёлая артиллерия», которая необходима, когда нужно координировать доступ к ресурсам на уровне операционной системы, например, для реализации singleton-приложения, работы с общими файлами или аппаратными устройствами. Его главные преимущества — межпроцессность и рекурсивность, но за это приходится платить производительностью и сложностью управления.