Пожалуйста, объясните с Linux, перспективы Windows?
Я программирую на С#, будут ли эти два условия иметь значение. Пожалуйста, напишите как можно больше, с примерами и т.д.
Спасибо
Пожалуйста, объясните с Linux, перспективы Windows?
Я программирую на С#, будут ли эти два условия иметь значение. Пожалуйста, напишите как можно больше, с примерами и т.д.
Спасибо
Для Windows критические разделы имеют меньший вес, чем мьютексы.
Мьютексы могут быть разделены между процессами, но всегда приводят к системному вызову ядра, которое имеет некоторые накладные расходы.
Критические разделы могут использоваться только в рамках одного процесса, но имеют то преимущество, что они только переключаются в режим ядра в случае разногласий. Необычные приобретения, которые должны быть обычным делом, невероятно быстры. В случае разногласий они входят в ядро, чтобы ждать некоторого примитива синхронизации (например, событие или семафор).
Я написал приложение для быстрого примера, которое сравнивает время между ними. В моей системе на 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 mutex - это алгоритм (а иногда и название структуры данных), который используется для защиты критических разделов.
Семафоры и Мониторы являются общими реализациями мьютекса.
На практике существует много реализаций mutex, доступных в окнах. Они в основном отличаются, как следствие их реализации, их уровнем блокировки, их областями, их расходами и их производительностью при разных уровнях конкуренции. См. CLR Inside Out - Используя concurrency для масштабируемости для диаграммы затрат на различные реализации мьютексов.
Доступные примитивы синхронизации.
Оператор lock(object)
реализуется с помощью Monitor
- см. MSDN для справки.
В последние годы большое количество исследований проводится по неблокирующей синхронизации. Цель состоит в том, чтобы реализовать алгоритмы без блокировки или без ожидания. В таких алгоритмах процесс помогает другим процессам завершить свою работу, чтобы процесс мог окончательно завершить свою работу. В результате процесс может завершить свою работу, даже если другие процессы, которые пытались выполнить какую-то работу, зависают. Usinig блокирует, они не будут освобождать свои блокировки и предотвращать продолжение других процессов.
Критический раздел и Mutex не являются специфичными для операционной системы, их понятиями многопоточности/многопроцессорности.
Критический раздел Является ли часть кода, которая должна выполняться только ею в любой момент времени (например, существует 5 потоков, работающих одновременно, и функция под названием "critical_section_function", которая обновляет массив... вы не хотите, чтобы все 5 потоков обновляли массив сразу. Поэтому, когда в программе выполняется критическая_секция_функции(), ни один из других потоков не должен запускать свою функцию critical_section_function.
мьютекс * Mutex - это способ реализации кода критического раздела (подумайте об этом как токен... поток должен обладать им для запуска критического_сезонного_кода)
В дополнение к другим ответам, следующие детали относятся к критическим разделам в окнах:
InterlockedCompareExchange
В linux я думаю, что у них есть "блокировка спина", которая аналогично относится к критическому разделу со счетчиком спинов.
Мьютекс - это объект, который может получить поток, не позволяя другим потокам его приобретать. Он является консультативным, а не обязательным; поток может использовать ресурс, который представляет собой мьютекс, не приобретая его.
Критический раздел - это длина кода, которая гарантируется операционной системой, чтобы не быть перепутаной. В псевдокоде это будет выглядеть так:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
"Быстрая" равность Windows для критического выбора в 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. Операции Mutex в основном реализуются в режиме ядра. Например, при создании Mutex вы вызываете nt! NtCreateMutant в ядре.
Отличный ответ от Майкла. Я добавил третий тест для класса mutex, введенного в С++ 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). Новый класс mutex в два раза быстрее, чем мьютекс Windows, но еще меньше, чем на десятую скорость объекта Windows CRITICAL_SECTION. Обратите внимание, что я тестировал только нерекурсивный мьютекс. Объекты CRITICAL_SECTION являются рекурсивными (один поток может вводить их повторно, при условии, что он оставляет одно и то же число раз).