Red de conocimiento informático - Material del sitio web - Cómo escribir subprocesos múltiples con c builder

Cómo escribir subprocesos múltiples con c builder

Resumen: este artículo presenta brevemente la importancia de la programación multiproceso en el entorno Windows, se centra en la cuestión del desarrollo de aplicaciones multiproceso en el entorno C Builder y ayuda a comprender el problema productor-consumidor. Entendemos mejor los conceptos de sincronización y cómo implementarlos.

Palabras clave: subprocesos múltiples; sincronización; productor-consumidor; C Builder

La viabilidad de los subprocesos

En muchos casos, puede ser necesario crear un subproceso. . Estas son algunas de las posibilidades:

(1) Si está creando un programa de interfaz de documentos múltiples (MDI), es muy importante asignar un hilo a cada ventana, por ejemplo, para un programa de comunicación MDI que está conectado a múltiples hosts a través de múltiples módems al mismo tiempo, si cada ventana tiene su propio hilo para comunicarse con un host, entonces todo será mucho más simple.

(2) Si está utilizando una máquina con múltiples procesadores y desea aprovechar al máximo todos los recursos de CPU posibles, debe dividir la aplicación en múltiples subprocesos. La unidad de división de la CPU en Windows 2000 es el hilo. Por lo tanto, si un programa contiene solo un subproceso, de forma predeterminada el programa solo puede usar una de las CPU. Sin embargo, si este programa se divide en varios subprocesos, Windows2000 puede ejecutar cada subproceso en diferentes CPU.

(3) Mientras ciertas tareas se ejecutan en segundo plano, los usuarios deben continuar usando la aplicación para trabajar. Esto se logra fácilmente usando hilos. Por ejemplo, algunos recálculos prolongados, operaciones de formato de página, lectura y escritura de archivos y otras actividades se pueden colocar en subprocesos separados para que puedan ejecutarse en segundo plano sin afectar al usuario.

Sincronización

Uno de los problemas más desafiantes al escribir programas multiproceso es: cómo hacer que un hilo coopere con otro hilo. Esto plantea un tema muy importante: la sincronización. La llamada sincronización se refiere a la capacidad de los procesos y subprocesos de evitar dañar sus respectivos datos al comunicarse entre sí. Los problemas de sincronización en el entorno Windows se deben al método de asignación de intervalos de tiempo de CPU del sistema Win32. Aunque solo un subproceso ocupa tiempo de CPU (CPU única) en un momento determinado, es imposible saber cuándo y dónde se interrumpió el subproceso, por lo que es particularmente importante cómo garantizar que los subprocesos no destruyan los datos de los demás. El problema de la sincronización es tan importante e interesante que ha atraído a muchos estudiosos a estudiarlo, dando como resultado una serie de problemas clásicos de sincronización de procesos, entre los que los más representativos son el "problema productor-consumidor", el "problema lector-escritor", "El problema de la comida de los filósofos", etc. Aquí, este artículo analiza brevemente cómo utilizar la tecnología de programación de subprocesos múltiples para implementar el problema "productor-consumidor" en la plataforma C Builder, ayudándonos a comprender mejor el concepto de sincronización y su método de implementación.

Problema Productor-Consumidor

El problema productor-consumidor es un problema de sincronización de procesos muy conocido. Lo que describe es: hay un grupo de procesos productores que producen mensajes y proporcionan este mensaje a los procesos consumidores para su consumo. Para permitir que el proceso productor y el proceso consumidor avancen al mismo tiempo, se configura un grupo de búfer con N búfer entre ellos. El proceso productor puede colocar los mensajes que produce en un búfer y el proceso consumidor puede obtener un mensaje de un. buffer para consumir. Aunque todos los procesos productores y consumidores operan de manera asincrónica, deben permanecer sincronizados, es decir, el proceso consumidor no puede recibir mensajes de un búfer vacío y el proceso productor no puede enviar mensajes a un búfer vacío. Coloca un mensaje en un búfer que ya está lleno de mensajes y aún no se ha eliminado.

Conceptos básicos de programación de aplicaciones multiproceso de C Builder

1. Utilice la clase TThread proporcionada por C Builder

La biblioteca de clases VCL proporciona la clase TThread para la programación de subprocesos. .

La clase TThread encapsula la API de Windows con respecto al mecanismo de subprocesos en Windows. Para la mayoría de las aplicaciones, los objetos de subproceso se pueden utilizar para representar subprocesos de ejecución en la aplicación. Los objetos de subprocesos simplifican la escritura de aplicaciones de subprocesos múltiples al encapsular lo que se necesita para usar subprocesos. Tenga en cuenta que los objetos de subprocesos no permiten controlar el tamaño de la pila de subprocesos o sus atributos de seguridad. Si necesita controlarlos, debe utilizar la función Create Thread() o Begin Thread() de la API de Windows.

La clase TThread tiene los siguientes atributos y métodos:

1) Atributos:

·Prioridad: atributo de prioridad. Se puede establecer la prioridad del hilo.

·Valor de retorno: Atributo de valor de retorno. Devuelve un valor a otros subprocesos cuando se introduce el subproceso.

·Suspended: Atributo suspendido. Puede determinar si el hilo está suspendido.

·Terminado: Atributo de fin. Se utiliza para marcar si el hilo debe terminarse.

·ThreadID: atributo del número de identificación. El número de identificación del hilo en todo el sistema. Esta propiedad es útil cuando se utilizan funciones API de Windows.

2) Método:

·Do Terminate: Genera un evento On Terminate, pero no finaliza la ejecución del hilo.

·Reanudar: Activa un hilo para continuar la ejecución.

·Suspender: Suspender un hilo y debe usarse junto con el proceso de Reanudar.

·Sincronizar: proceso de sincronización llamado por el hilo principal de VCL.

·Terminar: Establezca el atributo Terminar en True para finalizar la ejecución del hilo.

Esperar: espere a que el hilo termine y devuelva el valor del atributo Valor de retorno.

2. Coordinación de subprocesos

Al escribir código que se ejecute cuando se ejecuta un subproceso, debe tener en cuenta el comportamiento de otros subprocesos que pueden ejecutarse de forma sincrónica. Preste especial atención para evitar que dos subprocesos intenten utilizar el mismo objeto o variable global al mismo tiempo. Además, el código de un hilo puede depender de los resultados de las tareas realizadas por otros hilos.

1) Evitar el acceso simultáneo

Para evitar conflictos con otros subprocesos al acceder a objetos o variables globales, puede ser necesario suspender la ejecución de otros subprocesos hasta que el código del subproceso complete la operación. .

(1) Bloquear el objeto. Algunos objetos tienen capacidades de bloqueo integradas para evitar que otros subprocesos utilicen instancias del objeto. Por ejemplo, los objetos de lienzo (TCanvas y sus clases derivadas) tienen una función Lock() que evita que otros subprocesos accedan al lienzo hasta que se llame a la función Unlock(). Obviamente, este método sólo funciona para algunas clases.

(2) Utilice secciones importantes. Se pueden utilizar secciones importantes si el objeto no proporciona la funcionalidad de bloqueo incorporada. Las secciones importantes son como puertas, permitiendo que solo entre un hilo a la vez. Para usar secciones importantes, debe crear una instancia global de TCriticalSection. TCriticalSection tiene dos funciones: Acquire() (evitar que otros subprocesos ejecuten esta área) y Release() (desbloquear otros subprocesos).

(3) Utilice sincronizadores de lectura múltiple y escritura exclusiva. Cuando la memoria global está protegida mediante secciones críticas, solo un subproceso puede usar la memoria a la vez. Esta protección puede ser más de lo necesario, especialmente si tiene un objeto o variable que se lee con frecuencia pero que rara vez se escribe. No hay peligro de que varios subprocesos lean la misma memoria al mismo tiempo pero que ningún subproceso escriba en la memoria. Cuando hay algunas variables globales que a menudo se leen pero rara vez se escriben, puede usar el objeto TMultiReadExclusiveWriteSynchronizer para protegerlas. Este objeto es el mismo que la sección importante, pero permite que varios subprocesos lean al mismo tiempo, siempre que ningún subproceso esté escribiendo.

Cada subproceso que necesita leer la memoria primero debe llamar a la función Begin Read() (para garantizar que ningún otro subproceso esté escribiendo actualmente en la memoria). Una vez que el subproceso completa la operación de lectura de la memoria protegida, debe llamar a End Read(). ) función. Cualquier subproceso que necesite escribir en la memoria protegida debe llamar a la función Begin Write() (para garantizar que ningún otro subproceso esté leyendo o escribiendo en la memoria actualmente). Después de completar la operación de escritura en la memoria protegida, llame a la función End Write().

(4) Utilice la función Sincronizar: Void __fast call Synchronize (TThreadMethod amp; Method);

El parámetro Método es un nombre de proceso sin parámetros. Dentro de este procedimiento, que no requiere parámetros, hay un código que accede a la VCL. Podemos llamar al proceso Sincronizar durante el proceso Ejecutar para evitar el acceso simultáneo a VCL. El proceso específico durante la ejecución del programa en realidad es notificado por el proceso de sincronización al subproceso principal, y luego el subproceso principal ejecuta el proceso sin parámetros en la lista de parámetros del proceso de sincronización en el momento apropiado. En el caso de varios subprocesos, el subproceso principal coloca las notificaciones enviadas por el proceso de sincronización en la cola de mensajes y luego responde a estos mensajes uno por uno. A través de este mecanismo, Synchronize logra la sincronización entre subprocesos.

2) Esperando otros hilos

Si un hilo debe esperar a que otro hilo complete una tarea, el hilo puede ser interrumpido temporalmente. Luego, espere a que el otro subproceso complete la ejecución o espere a que se notifique a otro subproceso que la tarea se completó.

(1) Esperar el final de la ejecución del hilo

Para esperar el final de la ejecución de otro hilo, utilice su función Wait For (). La función Wait For no regresa hasta que ese hilo termina, ya sea al completar su función Execute() o debido a una excepción.

(2) Espere a que se complete la tarea. A veces, solo necesita esperar a que un hilo complete algunas operaciones en lugar de esperar a que finalice la ejecución del hilo. Para hacer esto, use un objeto de evento. Los objetos de evento (TEvent) deben tener un alcance global para que sean visibles para todos los subprocesos. Cuando un subproceso completa una operación que depende de otros subprocesos, se llama a la función TEvent::SetEvent(). Set Event emite una señal para que otros subprocesos puedan comprobar y saber que la operación se ha completado. Para apagar la señal, use la función Restablecer evento().

Por ejemplo, cuando varios hilos deben esperar para completar su ejecución en lugar de un solo hilo. Como no sabemos qué hilo se completó por última vez, no podemos usar la función Wait For () en un hilo determinado. En este punto, puede llamar a Set Event para acumular el valor de recuento cuando finaliza el hilo y enviar una señal cuando finaliza el último hilo para indicar el final de todos los hilos.

Ejemplo de programación de aplicaciones multiproceso

El siguiente es un ejemplo de aplicación multiproceso que implementa el "problema productor-consumidor". En este ejemplo, construimos dos subclases de TThread, TProducerThread (hilo de productor) y TCustomerThread (hilo de consumidor), de acuerdo con el método presentado anteriormente. El producto producido y consumido es solo un número entero. En el proceso de coordinación de producción y consumo, se utilizan secciones importantes (TCriticalSection) y eventos (TEvent). El productor notifica al consumidor que inicie el consumo a través del objeto Begin Consume de la clase TEvent, y el consumidor notifica al productor que inicie la producción a través del objeto Begin Produce de la clase TEent. Hay dos productores y un consumidor en el programa. La sincronización entre dos productores se realiza a través de objetos de la clase TCriticalSection. Su interfaz de ejecución se muestra en la Figura 1.

Figura 1 Efecto de ejecución del programa

El programa fuente principal es el siguiente:

Subproceso del productor:

Void __fast call TProducerThread:: Ejecutar ()

{

//---- Colocar el código del hilo aquí ----

Int i = 0

Int j;

while(ilt; 100) // Cada hilo productor produce 100 productos

{

Sleep(1000); muestra claramente el efecto de ejecución

if(Form1-gt; buffer_size gt; 0) // El grupo de búfer no está vacío, notifica al consumidor que lo consuma

{

Form1-gt; Comenzar Consumer-gt; Establecer evento ();

}

Form1-gt; Producir Guard-gt (); > i;

StrResult = IntToStr (i);

J = Form1-gt; buffer_size

Form1-gt;

Form1-gt; buffer_size;

Synchron

Reimpreso solo como referencia. Le deseo una vida feliz y acéptela si está satisfecho.