Domanda

Per favore, spiega da Linux, le prospettive di Windows?

Sto programmando in C #, questi due termini farebbero la differenza. Pubblica il più possibile, con esempi e simili ....

Grazie

È stato utile?

Soluzione

Per Windows, le sezioni critiche sono più leggere dei mutex.

I mutex possono essere condivisi tra i processi, ma comportano sempre una chiamata di sistema al kernel con un overhead.

Le sezioni critiche possono essere utilizzate solo all'interno di un processo, ma hanno il vantaggio di passare alla modalità kernel solo in caso di contesa: le acquisizioni indesiderate, che dovrebbero essere il caso comune, sono incredibilmente veloci. In caso di contesa, entrano nel kernel per attendere alcune primitive di sincronizzazione (come un evento o un semaforo).

Ho scritto un'app di esempio rapida che confronta il tempo tra i due. Sul mio sistema per 1.000.000 di acquisizioni e rilasci non programmati, un mutex richiede oltre un secondo. Una sezione critica richiede ~ 50 ms per 1.000.000 di acquisizioni.

Ecco il codice del test, l'ho eseguito e ho ottenuto risultati simili se mutex è il primo o il secondo, quindi non vediamo altri effetti.

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);

Altri suggerimenti

Dal punto di vista teorico, una sezione critica è un pezzo di codice che non deve essere eseguito da più thread contemporaneamente perché il codice accede alle risorse condivise.

Un mutex è un algoritmo (e talvolta il nome di una struttura di dati) che viene utilizzato per proteggere le sezioni critiche.

Semaphores e I monitor sono implementazioni comuni di un mutex.

In pratica ci sono molte implementazioni di mutex disponibili in Windows. Differiscono principalmente come conseguenza della loro implementazione per il loro livello di blocco, i loro scopi, i loro costi e le loro prestazioni in diversi livelli di contesa. Vedi CLR Inside Out - Utilizzo della concorrenza per la scalabilità per un grafico dei costi delle diverse implementazioni di mutex.

Primitive di sincronizzazione disponibili.

L'istruzione lock (object) è implementata usando un Monitor - vedi MSDN come riferimento.

Negli ultimi anni sono state fatte molte ricerche su sincronizzazione non bloccante . L'obiettivo è quello di implementare algoritmi in modo lock-free o wait-free. In tali algoritmi un processo aiuta altri processi a completare il loro lavoro in modo che il processo possa finalmente finire il suo lavoro. Di conseguenza un processo può terminare il suo lavoro anche quando altri processi, che hanno tentato di eseguire qualche lavoro, si bloccano. Usando i blocchi, non rilasciano i blocchi e impediscono che altri processi continuino.

Oltre alle altre risposte, i seguenti dettagli sono specifici delle sezioni critiche su Windows:

  • in assenza di contese, acquisire una sezione critica è semplice come un'operazione InterlockedCompareExchange
  • la struttura della sezione critica contiene spazio per un mutex. Inizialmente non allocato
  • in caso di contesa tra thread per una sezione critica, il mutex verrà allocato e utilizzato. Le prestazioni della sezione critica diminuiranno a quelle del mutex
  • se si prevede una forte contesa, è possibile allocare la sezione critica specificando un conteggio degli spin.
  • se c'è una contesa su una sezione critica con un conteggio di spin, il thread che tenta di acquisire la sezione critica girerà (occupato) per molti cicli di processore. Ciò può comportare prestazioni migliori rispetto alla modalità di sospensione, poiché il numero di cicli per eseguire un cambio di contesto su un altro thread può essere molto superiore al numero di cicli eseguiti dal thread proprietario per rilasciare il mutex
  • se il numero di giri scade, il mutex verrà assegnato
  • quando il thread proprietario rilascia la sezione critica, è necessario verificare se il mutex è allocato, in caso affermativo imposterà il mutex per rilasciare un thread in attesa

In Linux, penso che abbiano un " spin lock " che ha uno scopo simile alla sezione critica con un conteggio degli spin.

La sezione critica e Mutex non sono specifici del sistema operativo, i loro concetti di multithreading / multiprocessing.

Sezione critica È un pezzo di codice che deve essere eseguito da solo solo in un determinato momento (ad esempio, ci sono 5 thread in esecuzione contemporaneamente e una funzione chiamata " critical_section_function " che aggiorna un array ... non vuoi aggiornare tutti e 5 i thread l'array in una sola volta. Pertanto, quando il programma esegue critical_section_function (), nessuno degli altri thread deve eseguire la funzione critical_section_function.

mutex * Mutex è un modo per implementare il codice della sezione critica (pensalo come un token ... il thread deve averne il possesso per eseguire il codice_section_ critico)

L'equivalente "veloce" di Windows della selezione critica in Linux sarebbe un futex , che sta per mutex spazio utente veloce. La differenza tra un futex e un mutex è che con un futex, il kernel viene coinvolto solo quando è richiesto l'arbitrato, quindi si risparmia il sovraccarico di parlare con il kernel ogni volta che si modifica il contatore atomico. Questo ... può far risparmiare un significativo tempo negoziando blocchi in alcune applicazioni.

Un futex può anche essere condiviso tra i processi, usando i mezzi che impiegheresti per condividere un mutex.

Sfortunatamente, i futex possono essere molto difficili da implementare (PDF). (Aggiornamento del 2018, non sono così spaventosi come lo erano nel 2009).

Oltre a ciò, è praticamente lo stesso su entrambe le piattaforme. Stai apportando aggiornamenti atomici e basati su token a una struttura condivisa in modo che (si spera) non causi fame. Ciò che rimane è semplicemente il metodo per realizzarlo.

Un mutex è un oggetto che un thread può acquisire, impedendo ad altri thread di acquisirlo. È consultivo, non obbligatorio; un thread può usare la risorsa rappresentata dal mutex senza acquisirla.

Una sezione critica è una lunghezza di codice che è garantita dal sistema operativo per non essere interrotta. In pseudo-codice, sarebbe come:

StartCriticalSection();
    DoSomethingImportant();
    DoSomeOtherImportantThing();
EndCriticalSection();

In Windows, una sezione critica è locale per il tuo processo. Un mutex può essere condiviso / accessibile attraverso i processi. Fondamentalmente, le sezioni critiche sono molto più economiche. Non posso commentare specificamente Linux, ma su alcuni sistemi sono solo alias per la stessa cosa.

Solo per aggiungere i miei 2 centesimi, le sezioni critiche sono definite come una struttura e le operazioni su di esse vengono eseguite nel contesto in modalità utente.

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

Considerando che i mutex sono oggetti del kernel (ExMutantObjectType) creati nella directory degli oggetti di Windows. Le operazioni di Mutex sono per lo più implementate in modalità kernel. Ad esempio, quando si crea un Mutex, si finisce per chiamare nt! NtCreateMutant nel kernel.

Ottima risposta di Michael. Ho aggiunto un terzo test per la classe mutex introdotta in C ++ 11. Il risultato è alquanto interessante e supporta ancora la sua originale approvazione di oggetti CRITICAL_SECTION per singoli processi.

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);

I miei risultati sono stati 217, 473 e 19 (nota che il mio rapporto di volte per gli ultimi due è approssimativamente paragonabile a quello di Michael, ma la mia macchina è almeno quattro anni più giovane della sua, quindi puoi vedere prove di una maggiore velocità tra 2009 e 2013, quando è uscito l'XPS-8700). La nuova classe mutex è due volte più veloce del mutex di Windows, ma comunque meno di un decimo della velocità dell'oggetto CRITICAL_SECTION di Windows. Nota che ho testato solo il mutex non ricorsivo. Gli oggetti CRITICAL_SECTION sono ricorsivi (un thread può inserirli ripetutamente, purché lasci lo stesso numero di volte).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top