Поясніть, будь ласка, з точки зору Linux, Windows?
Я програмую на C #, чи змінили б ці два терміни. Будь ласка, опублікуйте скільки завгодно, із прикладами та подібними…
Дякую
Поясніть, будь ласка, з точки зору Linux, Windows?
Я програмую на C #, чи змінили б ці два терміни. Будь ласка, опублікуйте скільки завгодно, із прикладами та подібними…
Дякую
Відповіді:
Для Windows критичні розділи мають меншу вагу, ніж мютекси.
Mutexes може бути розподілений між процесами, але завжди призводить до системного виклику до ядра, яке має деякий накладні витрати.
Критичні розділи можна використовувати лише в межах одного процесу, але мають ту перевагу, що вони переходять у режим ядра лише у випадку суперечки - Безперебійне придбання, що повинно бути звичайним випадком, надзвичайно швидко. У разі суперечки вони вводять ядро, щоб чекати на якийсь примітив синхронізації (наприклад, подію чи семафор).
Я написав швидкий зразок програми, який порівнює час між ними. У моїй системі на 1 000 000 безперебійних придбань та випусків, мютекс займає одну секунду. Критичний розділ займає ~ 50 мс на 1 000 000 придбань.
Ось код тесту, я запустив це і отримав подібні результати, якщо mutex є першим чи другим, тому інших ефектів ми не бачимо.
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);
З теоретичної точки зору критичний розділ - це фрагмент коду, який не повинен працювати відразу декількома потоками, оскільки код отримує доступ до спільних ресурсів.
Взаємне блокування являє собою алгоритм (а іноді і ім'я структури даних) , який використовується для захисту критичних секцій.
Семафори та Монітори є загальною реалізацією мутексу.
На практиці існує багато застосувань mutex, доступних у Windows. Вони в основному різняться як наслідок їх виконання рівнем замикання, масштабами, витратами та ефективністю в різних рівнях суперечок. Див. Розділ CLR Inside Out - Використання паралельності для масштабованості для діаграми витрат на різні реалізації mutex.
Доступні примітиви синхронізації.
Оператор lock(object)
реалізований за допомогою Monitor
- див. MSDN для довідки.
В останні роки проводиться багато досліджень щодо неблокуючої синхронізації . Метою є реалізація алгоритмів у режимі без блокування чи очікування. У таких алгоритмах процес допомагає іншим процесам закінчити свою роботу, щоб процес остаточно закінчив свою роботу. Внаслідок цього процес може закінчити свою роботу навіть тоді, коли інші процеси, які намагалися виконати якусь роботу, зависають. Замки Usinig, вони не звільнять свої замки і не дозволяють іншим процесам тривати.
На додаток до інших відповідей, наступні деталі стосуються критичних розділів Windows:
InterlockedCompareExchange
операціяУ Linux я вважаю, що у них є "фіксація спина", яка служить аналогічною для критичної секції з кількістю віджиму.
Критичний розділ та Mutex не є операційною системою, їх концепція багатопотокової / багатопроцесорної обробки.
Критичний розділ - це фрагмент коду, який повинен виконувати лише сам у будь-який момент часу (наприклад, 5 потоків одночасно працює і функція під назвою "критична_секція_функція", яка оновлює масив ... вам не потрібно всіх 5 потоків одразу оновлення масиву. Отож, коли програма працює критично_секція_функція (), жоден з інших потоків не повинен запускати свою критичну_секцію_функції.
mutex * Mutex - це спосіб реалізації критичного коду розділу (думайте про це як маркер ... у потоці має бути володіння ним для запуску критичного_секції_коду)
Мутекс - це об'єкт, який нитка може придбати, заважаючи іншим потокам отримувати його. Це дорадчий, не обов'язковий характер; потік може використовувати ресурс, який представляє mutex, не отримуючи його.
Критичний розділ - це довжина коду, яка гарантується, що операційна система не перебиватиметься. У псевдокоді це було б так:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
"Швидка" Windows, що дорівнює критичному вибору в Linux, буде футекс , який означає швидкий mutex простору користувачів. Різниця між futex та mutex полягає в тому, що з futex ядро втягується лише тоді, коли потрібен арбітраж, тому ви зберігаєте накладні розмови з ядром щоразу, коли атомний лічильник змінюється. Це .. може заощадити значну кількість часу на узгодження блокувань у деяких програмах.
Футекс також можна поділити між процесами, використовуючи засоби, які ви б використали для спільного використання мютексу.
На жаль, футекси можуть бути дуже складними для впровадження (PDF). (Оновлення 2018 року, вони не так страшні, як у 2009 році).
Крім того, він майже однаковий на обох платформах. Ви робите атомні, марковані оновлення спільної структури таким чином, що (сподіваємось) не викликає голоду. Залишається просто метод досягнення цього.
Просто для додання моїх 2 копійок критичні розділи визначаються як структура, а операції над ними виконуються в режимі користувача.
ntdll! _RTL_CRITICAL_SECTION + 0x000 DebugInfo: Ptr32 _RTL_CRITICAL_SECTION_DEBUG + 0x004 LockCount: Int4B + 0x008 рекурсійний кількість: Int4B + 0x00c Влаштування Нитка: Ptr32 недійсна + 0x010 LockSemaphore: Ptr32 void + 0x014 SpinCount: Uint4B
Тоді як mutex - це об’єкти ядра (ExMutantObjectType), створені в каталозі об'єктів Windows. Операції Mutex здебільшого реалізуються в режимі ядра. Наприклад, створюючи Mutex, ви закінчуєте викликати nt! NtCreateMutant в ядрі.
Чудова відповідь від Майкла. Я додав третій тест для класу mutex, введеного в 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). Новий клас mutex удвічі швидший за мутекс Windows, але все ж менший, ніж на десяту частину швидкості об’єкта Windows CRITICAL_SECTION. Зауважте, що я протестував лише нерекурсивний мютекс. Об'єкти CRITICAL_SECTION є рекурсивними (одна нитка може вводити їх повторно, за умови, що вона залишає однакову кількість разів).
Функції змінного струму називаються реєнтними, якщо він використовує лише фактичні параметри.
Функції повторного виклику можуть викликатись декількома потоками одночасно.
Приклад функції рецензії:
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;
}
Стандартна бібліотека strtok () не є ретентованною і не може бути використана одночасно двома або більше потоками.
Деякі SDK платформи постачаються з ретентованною версією strtok (), що називається strtok_r ();
Енріко Мільйоре