Quelle est la différence entre mutex et section critique?

Veuillez expliquer à partir de Linux, Windows perspectives?

Je programmation en C#, ces deux termes feraient - ils une différence? Veuillez poster autant que vous le pouvez, avec des exemples et autres....

Merci

121
demandé sur Zifre 2009-04-29 04:23:32

9 réponses

Pour Windows, les sections critiques sont plus légères que les Mutex.

Les mutex peuvent être partagés entre les processus, mais entraînent toujours un appel système au noyau qui a une certaine surcharge.

Les sections critiques ne peuvent être utilisées que dans un processus, mais ont l'avantage de ne passer en mode noyau qu'en cas de conflit - les acquisitions sans assistance, ce qui devrait être le cas commun, sont incroyablement rapides. En cas de conflit, ils entrent dans le noyau pour attendre sur certains les primitives de synchronisation (comme un événement ou un sémaphore).

J'ai écrit un exemple d'application rapide qui compare le temps entre les deux. Sur mon système pour 1 000 000 d'acquisitions et de rejets non surveillés, un mutex prend plus d'une seconde. Une section critique prend ~50 ms pour 1,000,000 acquiert.

Voici le code de test, j'ai couru ceci et j'ai obtenu des résultats similaires si mutex est Premier ou deuxième, donc nous ne voyons pas d'autres effets.

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);
213
répondu Michael 2009-04-29 01:21:07

D'un point de vue théorique, une section critique est un morceau de code qui ne doit pas être exécuté par plusieurs threads à la fois car le code accède à des ressources partagées.

Un mutex est un algorithme (et parfois le nom d'une structure de données) qui est utilisé pour protéger des sections critiques.

Les sémaphores et les moniteurs sont des implémentations communes d'un mutex.

En pratique, il existe de nombreuses implémentations mutex disponibles dans windows. Ils diffèrent principalement en conséquence de leur mise en œuvre par leur niveau de verrouillage, leurs étendues, leurs coûts et leurs performances sous différents niveaux de contention. Voir CLR Intérieur - Utilisation de la concurrence pour l'évolutivité pour un graphique des coûts des différentes implémentations mutex.

Synchronisation disponible primitives.

Le lock(object) énoncé est mis en œuvre à l'aide d'un Monitor - voir MSDN pour référence.

Au cours des dernières années, de nombreuses recherches ont été effectuées sur la synchronisation non bloquante. L'objectif est de mettre en œuvre des algorithmes dans un lock-libre ou manière sans attente. Dans de tels algorithmes, un processus aide d'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 essayé d'effectuer un travail, se bloquent. Usinig serrures, ils ne libéreraient pas leurs serrures et empêcher d'autres processus de continuer.

77
répondu Daniel Brückner 2013-01-15 17:41:58

Critical Section et Mutex ne sont pas spécifiques au système d'exploitation, leurs concepts de multithreading/multiprocessing.

Section Critique Est un morceau de code qui ne doit s'exécuter que par lui-même à un moment donné (par exemple, il y a 5 threads qui s'exécutent simultanément et une fonction appelée "critical_section_function" qui met à jour un tableau... vous ne voulez pas que tous les 5 threads mettent à jour le tableau à la fois. Donc, lorsque le programme exécute critical_section_function (), aucun des autres les threads doivent exécuter leur critical_section_function.

Mutex* Mutex est un moyen d'implémenter le code de section critique (pensez-y comme un jeton... le thread doit en avoir la possession pour exécuter le critical_section_code)

18
répondu The Unknown 2009-04-29 00:31:45

En plus des autres réponses, les détails suivants sont spécifiques aux sections critiques sur windows:

  • , en l'absence de contention, l'acquisition d'une section critique est aussi simple qu'un InterlockedCompareExchange opération
  • la structure de section critique contient de la place pour un mutex. Il est initialement non alloué
  • s'il y a conflit entre les threads pour une section critique, le mutex sera alloué et utilisé. La performance de la section critique se dégradera à celle de la mutex
  • Si vous prévoyez une contention élevée, vous pouvez allouer la section critique en spécifiant un nombre de spins.
  • s'il y a contention sur une section critique avec un nombre de spin, le thread essayant d'acquérir la section critique tournera (occupé-attente) pour autant de cycles de processeur. Cela peut entraîner de meilleures performances que le sommeil, car le nombre de cycles pour effectuer un changement de contexte vers un autre thread peut être beaucoup plus élevé que le nombre de cycles pris par le thread propriétaire pour libérer le mutex
  • si le nombre de spins expire, le mutex sera alloué
  • lorsque le thread propriétaire libère la section critique, il est nécessaire de vérifier si le mutex est alloué, si c'est le cas, il définira le mutex pour libérer un thread en attente

Sous linux, je pense qu'ils ont un "spin lock" qui sert un but similaire à la section critique avec un nombre de spins.

18
répondu 1800 INFORMATION 2009-04-29 01:03:14

Un mutex est un objet qu'un thread peut acquérir, empêchant les autres threads de l'acquérir. Il est consultatif, pas obligatoire; un thread peut utiliser la ressource le mutex représente sans l'acquérir.

Une section critique est une longueur de code garantie par le système d'exploitation pour ne pas être interrompue. En pseudo-code, ce serait comme:

StartCriticalSection();
    DoSomethingImportant();
    DoSomeOtherImportantThing();
EndCriticalSection();
13
répondu Zifre 2016-08-20 14:01:15

Les fenêtres "rapides" égales à la sélection critique sous Linux seraient un futex , qui signifie fast user space mutex. La différence entre un futex et un mutex est qu'avec un futex, le noyau n'est impliqué que lorsque l'arbitrage est requis, de sorte que vous économisez la surcharge de parler au noyau chaque fois que le compteur atomique est modifié. Un futex peut également être partagé entre les processus, en utilisant les moyens que vous utiliseriez pour partager un mutex.

Malheureusement, les futex peuvent être très difficile à implémenter (PDF).

Au-delà de cela, c'est à peu près la même chose sur les deux plates-formes. Vous effectuez des mises à jour atomiques, basées sur des jetons, d'une structure partagée d'une manière qui (espérons-le) ne provoque pas de famine. Ce qui reste, c'est simplement la méthode pour y parvenir.

12
répondu Tim Post 2009-04-29 14:57:12

Dans Windows, une section critique est locale à votre processus. Un mutex peut être partagé / accessible entre les 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.

6
répondu Promit 2009-04-29 00:25:45

Juste pour ajouter mes 2 cents, les Sections critiques sont définies comme une structure et les opérations sur eux sont effectuées dans le contexte du mode utilisateur.

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

Alors que mutex sont des objets du noyau (ExMutantObjectType) créés dans le répertoire d'objets Windows. Les opérations Mutex sont principalement implémentées en mode noyau. Par exemple, lors de la création D'un Mutex, vous finissez par appeler nt!NtCreateMutant dans le noyau.

5
répondu Martin 2009-04-29 01:34:45

Excellente réponse de Michael. J'ai ajouté un troisième test pour la classe mutex introduite en C++11. Le résultat est quelque peu intéressant, et supporte toujours son approbation originale 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 temps pour les deux derniers est à peu près comparable à celui de Michael, mais ma machine est au moins quatre ans plus jeune que la sienne, de sorte que vous pouvez voir des preuves d'augmentation de la vitesse entre 2009 et 2013, lorsque le XPS-8700 est sorti). La nouvelle classe mutex est deux fois plus rapide que le mutex Windows, mais toujours moins d'un dixième de la vitesse de L'objet Windows CRITICAL_SECTION. Notez que j'ai seulement testé le mutex non récursif. Les objets CRITICAL_SECTION sont récursifs (un thread peut les entrer à plusieurs reprises, à condition qu'il laisse le même nombre de fois).

1
répondu Stevens Miller 2016-08-15 18:31:20