Пожалуйста, объясните с точки зрения Linux, Windows?
Я программирую на C #, будут ли эти два термина иметь значение. Пожалуйста, постите как можно больше, с примерами и тому подобным ....
Спасибо
Пожалуйста, объясните с точки зрения Linux, Windows?
Я программирую на C #, будут ли эти два термина иметь значение. Пожалуйста, постите как можно больше, с примерами и тому подобным ....
Спасибо
Ответы:
Для 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);
С теоретической точки зрения критический раздел - это фрагмент кода, который не должен выполняться несколькими потоками одновременно, поскольку код обращается к общим ресурсам.
Взаимная блокировка представляет собой алгоритм (а иногда и имя структуры данных) , который используется для защиты критических секций.
Семафоры и мониторы являются распространенными реализациями мьютекса.
На практике существует много реализаций мьютекса в Windows. В основном они различаются в результате своей реализации уровнем блокировки, областями их действия, стоимостью и эффективностью при разных уровнях конкуренции. См. CLR Inside Out - Использование параллелизма для масштабируемости для диаграммы затрат на различные реализации мьютекса.
Доступные примитивы синхронизации.
Оператор lock(object)
реализован с использованием Monitor
- см. MSDN для справки.
В последние годы много исследований делается на неблокирующую синхронизацию . Цель состоит в том, чтобы реализовать алгоритмы без блокировки или без ожидания. В таких алгоритмах процесс помогает другим процессам завершить свою работу, чтобы процесс мог окончательно завершить свою работу. В результате процесс может завершить свою работу, даже если другие процессы, которые пытались выполнить какую-либо работу, зависают. Используя блокировки, они не снимают свои блокировки и не позволяют другим процессам продолжаться.
В дополнение к другим ответам, следующие детали относятся к критическим разделам на окнах:
InterlockedCompareExchange
операцияВ Linux, я думаю, что они имеют «спин-блокировку», которая служит для целей, аналогичных критической секции с счетчиком спинов.
Critical Section и Mutex не зависят от операционной системы, их концепции многопоточности / многопроцессорности.
Критическая секция - это фрагмент кода, который должен запускаться только самостоятельно в любой момент времени (например, одновременно запущено 5 потоков и функция «variable_section_function», которая обновляет массив ... вам не нужны все 5 потоков обновлять массив сразу. Поэтому, когда программа запускает crit_section_function (), ни один из других потоков не должен запускать свою критическую функцию_секции.
mutex * Mutex - это способ реализации кода критической секции (думайте о нем, как о токене ... поток должен обладать им, чтобы запускать критический_секционный_код)
Мьютекс - это объект, который может получить поток, не позволяя другим потокам получить его. Это консультативный, а не обязательный; поток может использовать ресурс, который представляет мьютекс, не получая его.
Критическая секция - это длина кода, гарантируемая операционной системой, чтобы он не прерывался. В псевдокоде это будет выглядеть так:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
«Быстрый» Windows, равный критическому выбору в Linux, был бы futex , что означает быстрый мьютекс пространства пользователя. Разница между futex и mutex заключается в том, что с futex ядро включается только тогда, когда требуется арбитраж, поэтому вы экономите накладные расходы на общение с ядром каждый раз, когда изменяется атомный счетчик. Это может сэкономить значительное количество времени при согласовании блокировок в некоторых приложениях.
Футекс также может быть разделен между процессами, используя средства, которые вы бы использовали для совместного использования мьютекса.
К сожалению, фьютексы могут быть очень сложными для реализации (PDF). (Обновление 2018 года, они не так страшны, как в 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 в ядре.
Отличный ответ от Майкла. Я добавил третий тест для класса мьютекса, представленного в 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 являются рекурсивными (один поток может вводить их повторно, при условии, что он выходит одинаковое количество раз).
Функции AC называются реентерабельными, если они используют только свои фактические параметры.
Повторно входящие функции могут вызываться несколькими потоками одновременно.
Пример реентерабельной функции:
int reentrant_function (int a, int b)
{
int c;
c = a + b;
return c;
}
Пример не реентерабельной функции:
int result;
void non_reentrant_function (int a, int b)
{
int c;
c = a + b;
result = c;
}
Стандартная библиотека C strtok () не является реентерабельной и не может использоваться двумя или более потоками одновременно.
Некоторые SDK для платформ поставляются с возвращающейся версией strtok (), называемой strtok_r ();
Энрико Мильоре