В чем разница между мьютексом и критическим разделом? - PullRequest
127 голосов
/ 29 апреля 2009

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

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

Спасибо

Ответы [ 9 ]

218 голосов
/ 29 апреля 2009

Для Windows критические секции легче, чем мьютексы.

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

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

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

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

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);
83 голосов
/ 29 апреля 2009

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

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

Семафоры и Мониторы являются распространенными реализациями мьютекса.

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

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

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

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

20 голосов
/ 29 апреля 2009

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

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

В linux я думаю, что они имеют «спин-блокировку», которая выполняет те же функции, что и критическая секция с счетчиком спинов.

18 голосов
/ 29 апреля 2009

Critical Section и Mutex не зависят от операционной системы, их концепции многопоточности / многопроцессорности.

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

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

14 голосов
/ 29 апреля 2009

«Быстрый» Windows, равный критическому выбору в Linux, будет futex , что означает быстрый мьютекс пространства пользователя. Разница между futex и mutex заключается в том, что с futex ядро ​​включается только тогда, когда требуется арбитраж, поэтому вы экономите издержки на общение с ядром каждый раз, когда атомный счетчик изменяется. Это .. может сэкономить значительное количество времени согласования блокировок в некоторых приложениях.

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

К сожалению, фьютексы могут быть очень сложными для реализации (PDF). (Обновление 2018 года, они не так страшны, как в 2009 году).

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

13 голосов
/ 29 апреля 2009

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

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

StartCriticalSection();
    DoSomethingImportant();
    DoSomeOtherImportantThing();
EndCriticalSection();
6 голосов
/ 29 апреля 2009

Просто чтобы добавить мои 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. Операции мьютекса в основном реализованы в режиме ядра. Например, при создании Mutex вы в конечном итоге вызываете nt! NtCreateMutant в ядре.

6 голосов
/ 29 апреля 2009

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

1 голос
/ 15 августа 2016

Отличный ответ от Майкла. Я добавил третий тест для класса мьютекса, представленного в 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 являются рекурсивными (один поток может вводить их повторно при условии, что он выходит одинаковое количество раз).

...