El mecanismo de control actualmente popular para la sincronización y exclusión mutua de procesos y subprocesos en realidad se implementa mediante los cuatro métodos más primitivos y básicos. Al combinar y optimizar estos cuatro métodos, tenemos un método de control de procesos de subprocesos flexible y fácil de programar en .Net y Java.
Las definiciones específicas de estos cuatro métodos son las siguientes. Puede encontrar una explicación más detallada en el libro "Tutorial del sistema operativo" ISBN 7-5053-6193-7
1. Sección crítica: Pasar La serialización de subprocesos múltiples para acceder a recursos públicos o un fragmento de código es rápida y adecuada para controlar el acceso a datos.
2. Mutex: Diseñado para coordinar el acceso independiente a un recurso compartido.
3. Semáforo: Diseñado para controlar un número limitado de recursos del usuario.
4. Evento: se utiliza para notificar al hilo que han ocurrido algunos eventos, iniciando así la tarea posterior.
Sección Crítica
Una forma sencilla de garantizar que solo un hilo pueda acceder a los datos en un momento determinado. Sólo un hilo puede acceder a recursos compartidos en cualquier momento. Si varios subprocesos intentan acceder a la sección crítica al mismo tiempo, después de que un subproceso ingrese, todos los demás subprocesos que intenten acceder a la sección crítica se suspenderán hasta que el subproceso que ingresa a la sección crítica salga. Una vez liberada la sección crítica, otros subprocesos pueden continuar adelantándose a ella, logrando así el propósito de operar recursos compartidos de forma atómica.
La sección crítica contiene dos primitivas de operación:
EnterCriticalSection() ingresa a la sección crítica
LeaveCriticalSection() sale de la sección crítica
EnterCriticalSection Después de ejecutar la instrucción (), el código ingresará a la sección crítica. Pase lo que pase, se debe garantizar que se pueda ejecutar la LeaveCriticalSection() correspondiente. De lo contrario, los recursos compartidos protegidos por la sección crítica nunca serán liberados. Aunque la sincronización de la sección crítica es muy rápida, solo se puede usar para sincronizar subprocesos dentro de este proceso y no se puede usar para sincronizar subprocesos en múltiples procesos.
MFC proporciona muchas clases completamente funcionales. Utilicé MFC para implementar la sección crítica. MFC proporciona una clase CCriticalSection para secciones críticas. Es muy sencillo utilizar esta clase para la sincronización de subprocesos. Simplemente use las funciones miembro de la clase CCriticalSection Lock() y UnLock() en la función de subproceso para demarcar el fragmento de código protegido. Los recursos utilizados por el código después de Lock() se consideran automáticamente recursos en la sección crítica y están protegidos. Solo después de Desbloquear otros subprocesos pueden acceder a estos recursos.
//CriticalSection
CCriticalSection global_CriticalSection;
// ***Recursos compartidos
char global_Array[256]
//Inicializar recursos compartidos
void InitializeArray()
{
for(int i = 0; ilt; 256; i ) p>
{
global_Array[i]=I;
}
}
//Hilo de escritura
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam
ptr-gt;
//Ingrese la sección crítica
global_CriticalSection.Lock();
for(int i = 0; ilt; 256; i )
{
global_Array[i]=W;
ptr-gt; SetWindowText(global_Array);
Sueño(10); >
}
//Salir de la sección crítica
global_CriticalSection.Unlock();
return 0
}
//Eliminar hilo
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam ; p>
ptr-gt;SetWindowText("");
//Ingrese la sección crítica
global_CriticalSection.Lock()
for ( int i = 0; ilt; 256; i )
{
global_Array[i]=D
SetWindowText(global_Array); /p>
Sleep(10);
}
//Salir de la sección crítica
global_CriticalSection.Unlock(); p>return 0;
}
//Crear un hilo e iniciar el hilo
void CCriticalSectionsDlg::OnBnClickedButtonLock()
{
//Iniciar el primer hilo
CWinThread *ptrWrite = AfxBeginThread(Global_ThreadWrite,
amp;m_Write,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrWrite-gt;
/Iniciar el segundo hilo
CWinThread *ptrDelete = AfxBeginThread(Global_ThreadDelete,
amp;m_Delete,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrDelete-gt; ResumeThread();
}
En el programa de prueba, los dos botones Bloquear y Desbloquear. son respectivamente Implementación, el estado de ejecución de recursos compartidos con protección de sección crítica y el estado de ejecución de recursos compartidos sin protección de sección crítica.
Resultados de la ejecución del programa
Mutex (Mutex)
Un mutex es muy similar a una sección crítica. Solo el hilo propietario del objeto mutex tiene acceso a los recursos. Permisos, dado que solo hay un objeto mutex, determina que varios subprocesos no accederán a este recurso compartido al mismo tiempo bajo ninguna circunstancia. El subproceso que actualmente ocupa el recurso debe entregar el mutex que posee después de procesar la tarea, para que otros subprocesos puedan acceder al recurso después de adquirirlo. Los mutex son más complejos que las secciones críticas. Porque el uso de la exclusión mutua no solo puede lograr un intercambio seguro de recursos en diferentes subprocesos de la misma aplicación, sino también un intercambio seguro de recursos entre subprocesos de diferentes aplicaciones.
Mutex contiene varias operaciones primitivas:
CreateMutex() crea un mutex
OpenMutex() abre un mutex
p>ReleaseMutex( ) libera el mutex
WaitForMultipleObjects() espera el objeto mutex
Del mismo modo, MFC proporciona una clase CMutex para mutex. Es muy sencillo utilizar la clase CMutex para implementar operaciones mutex, pero preste especial atención a la llamada al constructor CMutex
CMutex(BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)
No complete los parámetros no utilizados indiscriminadamente. Completar los parámetros no utilizados provocará algunos resultados de operación inesperados.
//Crear un mutex
CMutex global_Mutex(0, 0, 0);
//*** Compartir recursos
char global_Array[256];
void InitializeArray()
{
for(int i = 0; ilt; 256; i )
{
global_Array[i]=I
}
}
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr-gt; SetWindowText("");
global_Mutex.Lock( )
for(int i = 0; ilt; 256; i )
{
global_Array[i]=W
ptr-gt; SetWindowText(global_Array);
Dormir(10);
}
global_Mutex.Unlock(); 0;
}
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam < / p>
ptr-gt; SetWindowText("");
global_Mutex.Lock()
for(int i = 0; ilt; 256; i ) < / p>
{
global_Array[i]=D;
ptr-gt; SetWindowText(global_Array);
Sueño(10); p>
}
global_Mutex.Unlock();
return 0;
}
También en el programa de prueba, Bloquear Desbloquear se implementan dos botones respectivamente, en el estado de ejecución de recursos compartidos protegidos por mutex y en el estado de ejecución de recursos compartidos sin protección mutex.
Resultados de la ejecución del programa
Semáforos
La forma en que el objeto semáforo sincroniza los subprocesos es diferente de los métodos anteriores. La señal permite que varios subprocesos se sincronicen al mismo tiempo. Utilice recursos compartidos, que es lo mismo que las operaciones PV en el sistema operativo. Señala la cantidad máxima de subprocesos que pueden acceder a recursos compartidos al mismo tiempo. Permite que varios subprocesos accedan al mismo recurso al mismo tiempo, pero debe limitar la cantidad máxima de subprocesos que pueden acceder a este recurso al mismo tiempo. Al crear un semáforo con CreateSemaphore(), también debe indicar el recuento máximo de recursos permitido y el recuento de recursos disponibles actualmente. Generalmente, el recuento actual de recursos disponibles se establece en el recuento máximo de recursos. Cada vez que un subproceso adicional accede al recurso compartido máximo, el recuento actual de recursos disponibles se reducirá en 1. Siempre que el recuento actual de recursos disponibles sea mayor que 0, Se puede enviar la señal del semáforo. Sin embargo, cuando el recuento disponible actual disminuye a 0, significa que la cantidad de subprocesos que actualmente ocupan recursos ha alcanzado el número máximo permitido y no se puede permitir la entrada de otros subprocesos. En este momento, no se enviará la señal del semáforo.
Después de que el hilo haya procesado los recursos compartidos, debería aumentar el recuento de recursos disponibles actualmente en 1 a través de la función ReleaseSemaphore() al salir. El recuento actual de recursos disponibles nunca puede ser mayor que el recuento máximo de recursos en ningún momento.
Los conceptos de operación fotovoltaica y semáforo fueron propuestos por el científico holandés E.W. Dijkstra. El semáforo S es un número entero cuando S es mayor o igual a cero, representa la cantidad de entidades de recursos disponibles para procesos concurrentes, pero cuando S es menor que cero, representa la cantidad de procesos que están esperando para usar el compartido. recursos.
La operación P se aplica a los recursos:
(1) S se reduce en 1
(2) Si S sigue siendo mayor o igual a cero después; al reducirse en 1, el proceso continúa ejecutándose;
(3) Si S es menor que cero después de menos 1, el proceso se bloqueará e ingresará a la cola correspondiente a la señal, y luego se transferirá al proceso. programación.
La operación V libera recursos:
(1) S se incrementa en 1
(2) Si el resultado de la suma es mayor que cero, el proceso continúa; ejecutar;
p>
(3) Si el resultado de la suma es menor o igual a cero, active un proceso en espera de la cola de espera de la señal y luego regrese al proceso original. para continuar la ejecución o transferir a la programación del proceso.
Semaphore contiene varias primitivas de operación:
CreateSemaphore() crea un semáforo
OpenSemaphore() abre un semáforo
ReleaseSemaphore() Lanzamiento el semáforo
WaitForSingleObject() Espere el semáforo
//Manija del semáforo
HANDLE global_Semephore
// ***Compartido recursos
char global_Array[256]
void InitializeArray()
{
for(int i = 0; ilt; 256 ; i )
{
global_Array[i]=I
}
}
// Hilo 1
UINT Global_ThreadOne(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam
ptr-gt; ; SetWindowText("");
//Esperar a que la solicitud de recurso compartido pase a través de la operación P igual
WaitForSingleObject(global_Semephore, INFINITE); >for(int i = 0; ilt; 256; i )
{
global_Array[i]=O
ptr- gt; );
Sleep(10);
}
//Liberar recursos compartidos igual a la operación V
ReleaseSemaphore(global_Semephore, 1). , NULL);
devuelve 0
}
UINT Global_ThreadTwo(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr-gt; SetWindowText("");
WaitForSingleObject(global_Semephore, INFINITO);
for(int i = 0; ilt; 256; i )
{
global_Array[i]=T
ptr-gt; (global_Array);
Sueño(10);
}
ReleaseSemaphore(global_Semephore, 1, NULL);
}
UINT Global_ThreadThree(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam
;p>
ptr-gt;SetWindowText("");
WaitForSingleObject(global_Semephore
, INFINITO);
for(int i = 0; ilt; 256; i )
{
global_Array[i]=H
;ptr-gt; SetWindowText(global_Array);
Sueño(10);
}
ReleaseSemaphore(global_Semephore, 1, NULL); p>
p>
return 0;
}
void CSemaphoreDlg::OnBnClickedButtonOne()
{
//Establece el semáforo Sólo un hilo puede acceder a 1 recurso 1 al mismo tiempo
global_Semephore= CreateSemaphore(NULL, 1, 1, NULL
this-gt;
// TODO: agregue aquí el código del controlador de notificaciones de control
}
void CSemaphoreDlg::OnBnClickedButtonTwo()
{
//Establece el recurso 2 del semáforo y solo puede acceder a él mediante dos subprocesos al mismo tiempo
global_Semaphore= CreateSemaphore(NULL, 2, 2, NULL
);this-gt; StartThread ();
// TODO: Agregue el código del controlador de notificaciones de control aquí
}
void CSemaphoreDlg::OnBnClickedButtonThree( )
{
//Establece el semáforo 3 recursos 3 y solo tres subprocesos pueden acceder a ellos al mismo tiempo
global_Semephore= CreateSemaphore(NULL, 3, 3 , NULL);
this-gt; StartThread();
// TODO: Agregue su código de controlador de notificaciones de control aquí
}
Las características de uso de los semáforos los hacen más adecuados para sincronizar subprocesos en programas de Socket. Por ejemplo, el servidor HTTP en la red necesita limitar la cantidad de usuarios que pueden acceder a la misma página al mismo tiempo. En este caso, se puede configurar un hilo para la solicitud de página de cada usuario al servidor, y la página es. un recurso compartido a proteger Al utilizar semáforos para sincronizar subprocesos, se puede garantizar que, sin importar cuántos usuarios accedan a una determinada página en cualquier momento, solo puedan acceder los subprocesos que no excedan el número máximo establecido de usuarios, mientras que otros intentos de acceso se realizarán. suspendido. El acceso sólo es posible después de que un usuario haya cerrado sesión en esta página.
Resultados de la ejecución del programa
Evento (Evento)
Los objetos de evento también pueden mantener la sincronización de subprocesos a través de operaciones de notificación. Y puede realizar operaciones de sincronización de subprocesos en diferentes procesos.
Semaphore contiene varias primitivas de operación:
CreateEvent() crea un semáforo
OpenEvent() abre un evento
SetEvent() restablece el evento
WaitForSingleObject() espera un evento
WaitForMultipleObjects() espera múltiples eventos
Prototipo de función WaitForMultipleObjects:
WaitForMultipleObjects(
IN DWORD nCount, // Número de identificadores de espera
IN CONST HANDLE *lpHandles, // Apuntando a la matriz de identificadores
IN BOOL bWaitAll, / / Si esperar completamente la bandera
IN DWORD dwMillisegundos //Tiempo de espera
)
El parámetro nCount especifica la cantidad de objetos del kernel a esperar y almacena estos objetos del kernel. La matriz es apuntada por lpHandles. fWaitAll especifica dos métodos de espera para los objetos del kernel nCount especificados. Cuando es VERDADERO, la función regresará cuando se notifique a todos los objetos. Cuando sea FALSO, puede regresar siempre que cualquiera de ellos sea notificado. La función de dwMillisegundos aquí es exactamente la misma que en WaitForSingleObject(). Si se agota el tiempo de espera, la función devuelve WAIT_TIMEOUT.
//Matriz de eventos
HANDLE global_Events[2];
// ***Recursos compartidos
char global_Array[256]
void InitializeArray()
{
for(int i = 0;ilt;256;i)
{ p>
p>
global_Ar
ray[i]=I
}
}
UINT Global_ThreadOne; (LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam
ptr-gt("");
for( int i = 0; ilt; 256; i )
{
global_Array[i]=O
ptr-gt; (global_Array);
Sleep(10);
}
//Restablecer evento
SetEvent(global_Events[0]);
devuelve 0;
}
UINT Global_ThreadTwo(LPVOID pParam)
{
CEdit *ptr= (CEdit *) pParam;
ptr-gt; SetWindowText("");
for(int i = 0; ilt; 256; i )
{
global_Array[i]=T
ptr-gt; SetWindowText(global_Array);
Sueño(10); }
//Restablecer evento
SetEvent(global_Events[1]);
return 0; p>UINT Global_ThreadThree(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam
ptr-gt;
//Espera a que se restablezcan ambos eventos
WaitForMultipleObjects(2, global_Events, true, INFINITE);
for(int i = 0; ilt; 256; i )
{
global_Array[i]=H
ptr-gt; (10 );
}
devuelve 0
}
void CEventDlg::OnBnClickedButtonStart()
{
for (int i = 0; i lt; 2; i)
{
//evento de instanciación
glob
al_Events[i]=CreateEvent(NULL, false, false, NULL);
}
CWinThread *ptrOne = AfxBeginThread(Global_ThreadOne,
amp; m_One,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED
ptrOne-gt(); //Iniciar el segundo hilo
CWinThread *ptrTwo = AfxBeginThread(Global_ThreadTwo,
amp;m_Two,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrTwo-gt; ResumeThread();
//Iniciar el tercer hilo
CWinThread *ptrThree = AfxBeginThread. (Global_ThreadThree,
&m_Three,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED
ptrThree -gt); ; ResumeThread();
// TODO: agregue su código de controlador de notificación de control aquí
}
Los eventos pueden realizar operaciones de sincronización de subprocesos en diferentes procesos y pueden Implemente fácilmente operaciones de espera de comparación de prioridad de múltiples subprocesos, como escribir múltiples WaitForSingleObjects en lugar de WaitForMultipleObjects para hacer la programación más flexible.
Resultados de la ejecución del programa
Resumen:
1. La función de un mutex es muy similar a la de una sección crítica, pero el mutex puede tener un nombre, lo que significa que se puede utilizar en todos los procesos. Por lo tanto, crear un mutex requiere más recursos, por lo que usar la sección crítica traerá ventajas de velocidad y reducirá el uso de recursos si solo se usa dentro del proceso. Debido a que el mutex es un mutex entre procesos, una vez creado, se puede abrir por su nombre.
2. Mutex (Mutex), semáforo (Semaphore) y evento (Evento) se pueden usar en todos los procesos para realizar operaciones de datos sincronizados. Otros objetos no tienen nada que ver con las operaciones de sincronización de datos, sino con procesos y subprocesos, si los procesos y los subprocesos En el. estado de ejecución, no hay estado de señal y, después de salir, hay estado de señal. Por lo tanto, puede usar WaitForSingleObject para esperar a que salgan los procesos y subprocesos.
3. Los mutex se pueden utilizar para especificar recursos que se utilizarán exclusivamente, pero si existe una de las siguientes situaciones que no pueden ser manejadas por mutex, por ejemplo, un usuario ha comprado un sistema de base de datos con tres licencias de acceso simultáneo. El número de permisos de acceso determina. ¿Cuántos subprocesos / procesos pueden realizar operaciones de base de datos al mismo tiempo? En este momento, si usa un mutex, no hay forma de completar este requisito. Se puede decir que el objeto semáforo es un contador de recursos.
Pregunta:
En Linux, hay dos tipos de semáforos.
La primera categoría es la versión SVR4 (System V Release 4) de semáforos definidos por la API semget/semop/semctl. La segunda categoría es la interfaz POSIX definida por sem_init/sem_wait/sem_post/interfaces. Tienen la misma funcionalidad pero diferentes interfaces. En el kernel 2.4.x, la estructura de datos del semáforo se define como (include/asm/semaphore.h).
Pero no hay ninguna mención específica de mutex en Linux. Acabo de ver que mutex es un caso especial de semáforo cuando el número máximo de recursos del semáforo = 1, se puede acceder al mismo tiempo*. **El número de subprocesos que comparten recursos = 1 es el mutex. La definición de sección crítica también es vaga. No se encontró información relevante sobre el uso de subprocesos/procesos de procesamiento de eventos para sincronizar operaciones mutuamente excluyentes. Utilice GCC/G para compilar código C estándar en Linux. La operación de los semáforos es casi la misma que la programación de VC7 en Windows. Sin embargo, el trasplante de mutex, eventos y secciones críticas de Linux tiene éxito. no ha tenido éxito.
Por favor, acepte.