pthread programming languages language concurrent windows linux multithreading programming-languages

windows - programming - ¿Cuál es la diferencia entre mutex y sección crítica?



linux programming language (9)

Por favor explique desde las perspectivas de Linux, Windows?

Estoy programando en C #, estos dos términos marcarían la diferencia. Por favor publique todo lo que pueda, con ejemplos y tal ...

Gracias


Además de las otras respuestas, los siguientes detalles son específicos de las secciones críticas en Windows:

  • en ausencia de controversia, adquirir una sección crítica es tan simple como una operación InterlockedCompareExchange
  • la estructura de la sección crítica tiene espacio para un mutex. Inicialmente no está asignado
  • si hay conflicto entre hilos para una sección crítica, el mutex será asignado y utilizado. El rendimiento de la sección crítica se degradará a la del mutex
  • si anticipa una alta contención, puede asignar la sección crítica que especifica un recuento de vueltas.
  • si hay contención en una sección crítica con un recuento de vueltas, el hilo que intenta adquirir la sección crítica girará (ocupado-espera) para esos muchos ciclos de procesador. Esto puede dar como resultado un mejor rendimiento que dormir, ya que el número de ciclos para realizar un cambio de contexto a otro subproceso puede ser mucho mayor que el número de ciclos tomados por el subproceso propietario para liberar el mutex
  • si el recuento de vueltas expira, se asignará el mutex
  • cuando el hilo propietario libera la sección crítica, se requiere verificar si el mutex está asignado, si es así, establecerá el mutex para liberar un hilo en espera

En Linux, creo que tienen un "bloqueo de giro" que tiene un propósito similar al de la sección crítica con un recuento de vueltas.


Desde una perspectiva teórica, una sección crítica es una porción de código que no debe ser ejecutada por múltiples hilos a la vez porque el código accede a los recursos compartidos.

Un mutex es un algoritmo (y algunas veces el nombre de una estructura de datos) que se usa para proteger secciones críticas.

Semaphores y los Monitors son implementaciones comunes de un mutex.

En la práctica, hay muchas implementaciones mutex disponibles en Windows. Principalmente difieren como consecuencia de su implementación por su nivel de bloqueo, sus alcances, sus costos y su desempeño bajo diferentes niveles de contención. Consulte CLR Inside Out - Uso de concurrencia para la escalabilidad de un gráfico de los costos de diferentes implementaciones de mutex.

Primitivas de sincronización disponibles.

La instrucción de lock(object) se implementa utilizando un Monitor : consulte MSDN para referencia.

En los últimos años, se realizan muchas investigaciones sobre la sincronización sin bloqueo . El objetivo es implementar algoritmos en una forma libre de bloqueo o sin esperar. En dichos algoritmos, un proceso ayuda a otros procesos a finalizar su trabajo para que el proceso finalmente pueda finalizar su trabajo. En consecuencia, un proceso puede finalizar su trabajo incluso cuando otros procesos que intentaron realizar algún trabajo se cuelgan. Usinig locks, no liberarían sus bloqueos y evitarían que otros procesos continúen.


El Windows "rápido" igual a la selección crítica en Linux sería un futex , que significa espacio de usuario rápido mutex. La diferencia entre un futex y un mutex es que con un futex, el kernel solo se involucra cuando se requiere arbitraje, por lo que se ahorra la sobrecarga de hablar con el kernel cada vez que se modifica el contador atómico. Un futex también se puede compartir entre los procesos, usando los medios que se emplearían para compartir un mutex.

Desafortunadamente, los futex pueden ser muy difíciles de implementar (PDF).

Más allá de eso, es prácticamente el mismo en ambas plataformas. Está realizando actualizaciones atómicas, controladas por tokens a una estructura compartida de una manera que (afortunadamente) no causa inanición. Lo que queda es simplemente el método para lograr eso.


En Windows, una sección crítica es local para su proceso. Un mutex se puede compartir / acceder a través de procesos. Básicamente, las secciones críticas son mucho más baratas. No puedo comentar sobre Linux específicamente, pero en algunos sistemas son solo alias para la misma cosa.


Gran respuesta de Michael. He agregado una tercera prueba para la clase mutex presentada en C ++ 11. El resultado es algo interesante, y todavía es compatible con su aprobación original de objetos CRITICAL_SECTION para procesos individuales.

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

Mis resultados fueron 217, 473 y 19 (tenga en cuenta que mi relación de tiempos para los dos últimos es más o menos comparable a la de Michael, pero mi máquina es al menos cuatro años más joven que la suya, por lo que puede ver evidencia de mayor velocidad entre 2009 y 2013 , cuando salió el XPS-8700). La nueva clase mutex es dos veces más rápida que el mutex de Windows, pero aún menos de una décima parte de la velocidad del objeto Windows CRITICAL_SECTION. Tenga en cuenta que solo probé el mutex no recursivo. Los objetos CRITICAL_SECTION son recursivos (un hilo puede ingresarlos repetidamente, siempre que salga el mismo número de veces).


Para Windows, las secciones críticas son más ligeras que las exclusiones mutuas.

Los mutexes se pueden compartir entre procesos, pero siempre dan como resultado una llamada al kernel que tiene una sobrecarga.

Las secciones críticas solo se pueden usar en un proceso, pero tienen la ventaja de que solo cambian al modo kernel en caso de contención: las adquisiciones imprevistas, que deberían ser el caso común, son increíblemente rápidas. En el caso de contención, ingresan al kernel para esperar en alguna primitiva de sincronización (como un evento o semáforo).

Escribí una aplicación de muestra rápida que compara el tiempo entre los dos. En mi sistema para 1,000,000 adquisiciones y liberaciones no aprobadas, un mutex toma más de un segundo. Una sección crítica requiere ~ 50 ms para 1,000,000 adquiere.

Aquí está el código de prueba, ejecuté esto y obtuve resultados similares si mutex es el primero o el segundo, por lo que no estamos viendo ningún otro efecto.

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


Sección crítica y Mutex no son específicos del sistema operativo, sus conceptos de multiprocesamiento / multiproceso.

Sección crítica Es una porción de código que solo debe ejecutarse por sí mismo en cualquier momento dado (por ejemplo, hay 5 subprocesos ejecutándose simultáneamente y una función llamada "critical_section_function" que actualiza una matriz ... no desea los 5 hilos actualizando la matriz de una vez. Por lo tanto, cuando el programa ejecuta critical_section_function (), ninguno de los otros subprocesos debe ejecutar su critical_section_function.

mutex * Mutex es una forma de implementar el código de sección crítica (piénselo como un token ... el hilo debe tener posesión de él para ejecutar el critical_section_code)


Solo para agregar mis 2 centavos, las Secciones críticas se definen como una estructura y las operaciones sobre ellas se realizan en el contexto de modo de usuario.

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

Mientras que mutex son objetos kernel (ExMutantObjectType) creados en el directorio de objetos de Windows. Las operaciones Mutex se implementan principalmente en modo kernel. Por ejemplo, al crear un Mutex, terminas llamando a nt! NtCreateMutant en kernel.


Un mutex es un objeto que puede adquirir un hilo, lo que impide que otros hilos lo adquieran. Es consultivo, no obligatorio; un hilo puede usar el recurso que mutex representa sin adquirirlo.

Una sección crítica es una longitud de código garantizada por el sistema operativo para que no se interrumpa. En pseudo-código, sería como:

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