В чем разница между мьютексом и критической секцией?

пожалуйста, объясните из Linux, перспективы Windows?

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

спасибо

9 ответов


для Windows критические разделы легче мьютексов.

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

критические разделы могут использоваться только в рамках одного процесса, но имеют то преимущество, что они переключаются в режим ядра только в случае раздора - Uncontended acquires, что должно быть общим случаем, невероятно быстро. В случае разногласий они вводят ядро в подождите некоторого примитива синхронизации (например, события или семафора).

Я написал быстрый пример приложения, которое сравнивает время между ними двумя. В моей системе за 1.000.000 неконфликтным приобретает и отпускает, мьютекс берет за одну секунду. Критический раздел занимает ~50 мс для 1,000,000 приобретает.

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

HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;

// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    EnterCriticalSection(&critSec);
    LeaveCriticalSection(&critSec);
}

QueryPerformanceCounter(&end);

int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    WaitForSingleObject(mutex, INFINITE);
    ReleaseMutex(mutex);
}

QueryPerformanceCounter(&end);

int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);

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

A мьютекс алгоритм (а иногда и имя структуры данных), который используется для защиты критических секций.

семафоры и мониторы общие реализации мьютекса.

на практике существует много мьютексов реализация доступна в windows. Они в основном различаются в результате их реализации по уровню блокировки, их масштабам, их затратам и их производительности при разных уровнях конкуренции. См. CLR наизнанку - Использование параллелизма для масштабируемости для диаграммы затрат на различные реализации мьютексов.

синхронизации доступно примитивы.

на lock(object) оператор реализуется с помощью Monitor - см. в MSDN для справки.

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


критический раздел и мьютекс не специфичны для операционной системы, их концепции многопоточности/многопроцессорности.

Критическая Секция Это фрагмент кода, который должен выполняться только им в любой момент времени (например, одновременно выполняется 5 потоков и функция critical_section_function, которая обновляет массив... вы не хотите, чтобы все 5 потоков обновляли массив сразу. Поэтому, когда программа работает critical_section_function (), ни один из другие потоки должны запускать свою critical_section_function.

мьютекс* Мьютекс-это способ реализации кода критической секции (что это знак... поток должен обладать им, чтобы запустить critical_section_code)


в дополнение к другим ответам следующие сведения относятся к критическим разделам в windows:

  • в отсутствие раздора, приобретение критического раздела так же просто, как InterlockedCompareExchange операция
  • структура критического раздела содержит место для мьютекса. Он изначально нераспределен
  • если существует конфликт между потоками для критического раздела, мьютекс будет выделен и использован. Эффективность работы критической секции деградировать до мьютекса
  • если вы ожидаете высокой конкуренции, вы можете выделить критический раздел, указав количество спинов.
  • если есть разногласия по критическому разделу с количеством спинов, поток, пытающийся получить критический раздел, будет вращаться (занят-ждать) для этого количества циклов процессора. Это может привести к лучшей производительности, чем спящий режим, так как количество циклов для выполнения контекстного переключения в другой поток может быть намного больше, чем количество циклов принято владеющим потоком, чтобы освободить мьютекс
  • если счетчик спинов истекает, мьютекс будет выделен
  • когда собственный поток освобождает критический раздел, необходимо проверить, выделен ли мьютекс, если это так, то он установит мьютекс для освобождения ожидающего потока

в linux я думаю, что у них есть "блокировка спина", которая служит аналогичной цели критического раздела с количеством спинов.


мьютекс-это объект, который поток может получить, предотвращая его получение другими потоками. Он является консультативным, а не обязательным; поток может использовать ресурс, который представляет мьютекс, не приобретая его.

критический раздел-это длина кода, которая гарантируется операционной системой, чтобы не быть interupted. В псевдо-коде это было бы так:

StartCriticalSection();
    DoSomethingImportant();
    DoSomeOtherImportantThing();
EndCriticalSection();

"быстрые" окна, равные критическому выбору в Linux, будут futex, что означает быстрый мьютекс пользовательского пространства. Разница между futex и мьютексом заключается в том, что с futex ядро участвует только тогда, когда требуется арбитраж, поэтому вы сохраняете накладные расходы на разговор с ядром каждый раз, когда атомарный счетчик изменяется. Futex также может быть разделен между процессами, используя средства, которые вы бы использовали для совместного использования мьютекса.

к сожалению, futexes может быть очень сложно реализовать (PDF).

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


в Windows критический раздел является локальным для вашего процесса. Мьютекс можно совместно использовать / получить доступ к процессам. В принципе, критические разделы намного дешевле. Не могу комментировать Linux конкретно, но в некоторых системах они просто псевдонимы для одного и того же.


чтобы добавить мои 2 цента, критические разделы определяются как структура, и операции над ними выполняются в контексте пользовательского режима.

ntdll!_RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : Ptr32 _RTL_CRITICAL_SECTION_DEBUG
   +0x004 LockCount        : Int4B
   +0x008 RecursionCount   : Int4B
   +0x00c OwningThread     : Ptr32 Void
   +0x010 LockSemaphore    : Ptr32 Void
   +0x014 SpinCount        : Uint4B

тогда как мьютекс-это объекты ядра (ExMutantObjectType), созданные в каталоге объектов Windows. Операции мьютекса в основном реализуются в режиме ядра. Например, при создании мьютекса, вы в конечном итоге вызов НТ!NtCreateMutant в ядре.


отличный ответ от Михаила. Я добавил третий тест для класса мьютекса, введенного в C++11. Результат несколько интересен и по-прежнему поддерживает его первоначальное одобрение объектов CRITICAL_SECTION для отдельных процессов.

mutex m;
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;

// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    EnterCriticalSection(&critSec);
    LeaveCriticalSection(&critSec);
}

QueryPerformanceCounter(&end);

int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    WaitForSingleObject(mutex, INFINITE);
    ReleaseMutex(mutex);
}

QueryPerformanceCounter(&end);

int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
m.lock();
m.unlock();

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    m.lock();
    m.unlock();
}

QueryPerformanceCounter(&end);

int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);


printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS);

мои результаты были 217, 473 и 19 (обратите внимание, что мое соотношение раз за последние два примерно сопоставимо с Майклом, но моя машина по крайней мере на четыре года моложе его, поэтому вы можете увидеть доказательства увеличения скорости между 2009 и 2013 годами, когда вышел XPS-8700). Новый класс мьютекса в два раза быстрее, чем мьютекс Windows, но все же меньше десятой скорости объекта Windows CRITICAL_SECTION. Обратите внимание, что я тестировал только нерекурсивный мьютекс. Объекты CRITICAL_SECTION рекурсивны (один поток может вводить их повторно, при условии, что он оставляет одинаковое количество раз).