Veuillez expliquer du point de vue Linux, Windows?
Je programme en C #, ces deux termes feraient-ils une différence? S'il vous plaît poster autant que vous le pouvez, avec des exemples et autres ....
Merci
Veuillez expliquer du point de vue Linux, Windows?
Je programme en C #, ces deux termes feraient-ils une différence? S'il vous plaît poster autant que vous le pouvez, avec des exemples et autres ....
Merci
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, ce qui entraîne une 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 non contrôlées, qui devraient être le cas courant, 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 les deux. Sur mon système pour 1 000 000 d'acquisitions et de versions 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 exécuté ceci et j'ai obtenu des résultats similaires si mutex est premier ou deuxième, donc nous ne voyons 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, 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 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.
Les sémaphores et les moniteurs sont des implémentations courantes d'un mutex.
En pratique, de nombreuses implémentations de mutex sont disponibles dans Windows. Ils diffèrent principalement en raison de leur mise en œuvre par leur niveau de verrouillage, leurs portées, leurs coûts et leurs performances sous différents niveaux de contention. Voir CLR Inside Out - Utilisation de la concurrence pour l'évolutivité pour un graphique des coûts des différentes implémentations de mutex.
Primitives de synchronisation disponibles.
L' lock(object)
instruction est implémentée à l'aide d'un Monitor
- voir MSDN pour référence.
Ces 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 de manière sans verrouillage ou 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 certain travail, se bloquent. En utilisant des verrous, ils ne libéreraient pas leurs verrous et empêcheraient d'autres processus de se poursuivre.
En plus des autres réponses, les détails suivants sont spécifiques aux sections critiques sur Windows:
InterlockedCompareExchange
opérationSous Linux, je pense qu'ils ont un "verrouillage de rotation" qui sert un objectif similaire à la section critique avec un nombre de tours.
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 en cours d'exécution simultanément et une fonction appelée "critical_section_function" qui met à jour un tableau ... vous ne voulez pas les 5 threads mise à jour du tableau à la fois. Ainsi, lorsque le programme exécute la fonction critique_section_function (), aucun des autres threads ne doit exécuter sa fonction critique_section_function.
mutex * Mutex est un moyen d'implémenter le code de la section critique (pensez-y comme un jeton ... le thread doit en avoir la possession pour exécuter le critique_section_code)
Un mutex est un objet qu'un thread peut acquérir, empêchant d'autres threads de l'acquérir. Il est consultatif et non obligatoire; un thread peut utiliser la ressource représentée par le mutex 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();
L'égalité 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 est qu'avec un futex, le noyau n'est impliqué que lorsqu'un arbitrage est requis, donc vous économisez la surcharge de parler au noyau chaque fois que le compteur atomique est modifié. Ce .. peut enregistrer une importante quantité de verrous de négociation de temps dans certaines applications.
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 difficiles à implémenter (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 effectuez des mises à jour atomiques, pilotées par jeton, d'une structure partagée d'une manière qui (espérons-le) ne provoque pas de famine. Ce qui reste est simplement la méthode pour y parvenir.
Sous 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 faire de commentaires sur Linux en particulier, mais sur certains systèmes, ce ne sont que des alias pour la même chose.
Juste pour ajouter mes 2 cents, les sections critiques sont définies comme une structure et les opérations sur elles 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 + 0x00c OwningThread: Ptr32 Void + 0x010 LockSemaphore: Ptr32 Void + 0x014 SpinCount: Uint4B
Tandis que les mutex sont des objets 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.
Excellente 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 prend toujours en charge 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 temps pour les deux derniers est à peu près comparable à celui de Michael, mais ma machine a au moins quatre ans de moins que la sienne, vous pouvez donc voir des preuves d'une vitesse accrue 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 je n'ai testé que le mutex non récursif. Les objets CRITICAL_SECTION sont récursifs (un thread peut les saisir à plusieurs reprises, à condition de laisser le même nombre de fois).
Les fonctions AC sont appelées réentrantes si elles n'utilisent que leurs paramètres réels.
Les fonctions réentrantes peuvent être appelées par plusieurs threads en même temps.
Exemple de fonction réentrante:
int reentrant_function (int a, int b)
{
int c;
c = a + b;
return c;
}
Exemple de fonction non réentrante:
int result;
void non_reentrant_function (int a, int b)
{
int c;
c = a + b;
result = c;
}
La bibliothèque standard C strtok () n'est pas réentrante et ne peut pas être utilisée par 2 threads ou plus en même temps.
Certains SDK de plate-forme sont livrés avec la version réentrante de strtok () appelée strtok_r ();
Enrico Migliore