S'il vous plaît expliquer à partir de Linux, perspectives Windows?
Je programme en C #, ces deux termes pourraient-ils faire la différence? S'il vous plaît poster autant que vous le pouvez, avec des exemples et tels ....
Merci
Pour Windows, les sections critiques sont plus légères que les mutex.
Les mutex peuvent être partagés entre les processus, mais aboutissent toujours à un appel système au noyau qui présente une surcharge.
Les sections critiques ne peuvent être utilisées que dans un processus, mais présentent l'avantage de ne basculer en mode noyau qu'en cas de conflit - Les acquisitions non contrôlées, ce qui devrait être le cas habituel, sont incroyablement rapides. En cas de conflit, ils entrent dans le noyau pour attendre une primitive de synchronisation (comme un événement ou un sémaphore).
J'ai écrit un exemple d'application rapide qui compare le temps entre eux. Sur mon système pour 1 000 000 d'acquisitions et de sorties non contrôlées, un mutex prend plus d'une seconde. Une section critique prend environ 50 ms pour 1 000 000 d’acquisitions.
Voici le code de test, j'ai couru cela et obtenu des résultats similaires si mutex est premier ou deuxième, nous ne voyons donc aucun autre effet.
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);
D'un point de vue théorique, un section critique est un morceau de code qui ne doit pas être exécuté par plusieurs threads à la fois car le code accède aux ressources partagées.
Un mutex est un algorithme (et parfois le nom d'une structure de données) utilisé pour protéger les sections critiques.
Sémaphores et Moniteurs sont des implémentations courantes d'un mutex.
En pratique, de nombreuses implémentations mutex sont disponibles dans Windows. Ils diffèrent principalement en conséquence de leur mise en œuvre par leur niveau de verrouillage, leurs portées, leurs coûts et leurs performances à différents niveaux de conflit. Voir CLR Inside Out - Utilisation de la simultanéité pour l'évolutivité pour un graphique des coûts de différentes implémentations de mutex.
Primitives de synchronisation disponibles.
L'instruction lock(object)
est implémentée à l'aide de Monitor
- voir MSDN pour référence.
Au cours des dernières années, de nombreuses recherches ont été effectuées sur synchronisation non bloquante . L’objectif est d’implémenter des algorithmes sans verrouillage ni attente. Dans de tels algorithmes, un processus aide les autres processus à terminer leur travail afin que le processus puisse enfin terminer son travail. En conséquence, un processus peut terminer son travail même lorsque d’autres processus, qui ont tenté d’exécuter certains travaux, se bloquent. En utilisant les verrous, ils ne lâcheraient pas leurs verrous et empêcheraient d'autres processus de continuer.
Outre les autres réponses, les détails suivants sont spécifiques aux sections critiques de Windows:
InterlockedCompareExchange
Sous Linux, je pense qu’ils ont un "verrou de rotation" qui a un but similaire à celui de la section critique avec un compte de rotation.
Critical Section et Mutex ne sont pas spécifiques au système d'exploitation, leurs concepts de multithreading/multitraitement.
Critical Section Est un morceau de code qui ne doit être exécuté que par lui-même à tout moment (par exemple, 5 threads s'exécutent simultanément et une fonction appelée "critical_section_function" qui met à jour un tableau ... vous ne voulez pas que les 5 threads mettent à jour le tableau en même temps. Ainsi, lorsque le programme exécute critical_section_function (), aucun des autres threads ne doit exécuter son critical_section_function.
mutex * Le mutex est un moyen d'implémenter le code de section critique (pensez-le comme un jeton ... le thread doit en avoir la possession pour pouvoir exécuter le code de section (Critical_section_code).
L'équivalent Windows "rapide" de la sélection critique sous Linux serait un futex , qui signifie mutex d'espace utilisateur rapide. La différence entre un futex et un mutex réside dans le fait qu'avec un futex, le noyau n'intervient que lorsqu'un arbitrage est requis. Vous évitez ainsi de parler au noyau chaque fois que le compteur atomique est modifié. Cela .. peut économiser un temps important dans la négociation de verrous dans certaines applications.
Un futex peut également être partagé entre plusieurs processus, en utilisant les moyens que vous utiliseriez pour partager un mutex.
Malheureusement, les futex peuvent être très difficile à mettre en œuvre (PDF). (Mise à jour 2018, ils ne sont pas aussi effrayants qu’ils l’étaient en 2009).
Au-delà de cela, c'est à peu près la même chose sur les deux plates-formes. Vous apportez des mises à jour atomiques, basées sur des jetons, à une structure partagée d'une manière qui (espérons-le) ne provoque pas la famine. Ce qui reste est simplement la méthode pour y parvenir.
Un mutex est un objet qu'un thread peut acquérir, empêchant les autres threads de l'acquérir. C'est consultatif, pas obligatoire; un thread peut utiliser la ressource que le mutex représente sans l’acquérir.
Une section critique est une longueur de code que le système d'exploitation garantit de ne pas interrompre. En pseudo-code, ce serait comme:
StartCriticalSection();
DoSomethingImportant();
DoSomeOtherImportantThing();
EndCriticalSection();
Juste pour ajouter mes 2 centimes, les sections critiques sont définies comme une structure et leurs opérations sont effectuées dans un contexte en mode utilisateur.
ntdll! _RTL_CRITICAL_SECTION + 0x000 DebugInfo: Ptr32 _RTL_CRITICAL_SECTION_DEBUG + 0x004 LockCount: Int4B + 0x008 RecursionCount: Int4B + 0x010 LockSemaphore: Ptr32 nul + 0x014 SpinCount: Uint4B
Les mutex sont des objets du noyau (ExMutantObjectType) créés dans le répertoire des objets Windows. Les opérations mutex sont principalement implémentées en mode noyau. Par exemple, lorsque vous créez un mutex, vous appelez nt! NtCreateMutant dans le noyau.
Sous Windows, une section critique est locale pour votre processus. Un mutex peut être partagé/accessible entre plusieurs processus. Fondamentalement, les sections critiques sont beaucoup moins chères. Je ne peux pas commenter spécifiquement Linux, mais sur certains systèmes, ce ne sont que des alias pour la même chose.
Super réponse de Michael. J'ai ajouté un troisième test pour la classe mutex introduite dans C++ 11. Le résultat est quelque peu intéressant et conforte son approbation initiale des objets CRITICAL_SECTION pour des processus uniques.
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);
Mes résultats étaient 217, 473 et 19 (notez que mon ratio de fois pour les deux derniers est à peu près comparable à celui de Michael, mais que ma machine a au moins quatre ans de moins que la sienne, vous pouvez donc voir des signes d'augmentation de la vitesse entre 2009 et 2013. , lorsque le XPS-8700 est sorti). La nouvelle classe de mutex est deux fois plus rapide que le mutex Windows, mais reste inférieure au dixième de la vitesse de l'objet Windows CRITICAL_SECTION. Notez que je n'ai testé que le mutex non récursif. Les objets CRITICAL_SECTION sont récursifs (un thread peut les entrer de manière répétée, à condition qu'il laisse le même nombre de fois).