Cómo QT admite e implementa subprocesos en diferentes plataformas
Qt, como sistema GUI multiplataforma basado en C++, puede proporcionar a los usuarios potentes funciones para construir interfaces gráficas de usuario. Para satisfacer las necesidades de los usuarios en la construcción de sistemas de interfaz gráfica complejos, Qt proporciona un rico soporte de programación multiproceso. A partir de la versión 2.2, Qt proporciona principalmente soporte para programación multiproceso desde los siguientes tres aspectos: 1. Construcción de algunas clases de subprocesos básicas independientes de la plataforma 2. Método seguro para subprocesos para enviar eventos definidos por el usuario 3. Múltiples subprocesos; Mecanismos de sincronización, como semáforos y bloqueos globales. Estos brindan a los usuarios una gran comodidad. Sin embargo, en algunos casos, usar el mecanismo del temporizador puede lograr las funciones requeridas de manera más conveniente que usar el mecanismo de subprocesos múltiples propio de Qt y, al mismo tiempo, evitar fenómenos inseguros. Este artículo no solo analiza el mecanismo de soporte de subprocesos múltiples en Qt, sino que también se centra en el método de utilizar el mecanismo del temporizador para simular la programación de subprocesos múltiples.
1. Soporte del sistema para programación multiproceso
Diferentes plataformas admiten el multiproceso de Qt de diferentes maneras. Cuando los usuarios instalan el sistema Qt en el sistema operativo Windows, la compatibilidad con subprocesos es una opción para el compilador. El subdirectorio mkfiles de Qt incluye archivos compilados para diferentes tipos de compiladores. Los archivos con el sufijo -mt son los que admiten múltiples tipos de. compiladores.
En el sistema operativo Unix, la compatibilidad con subprocesos se agrega agregando la opción -thread al ejecutar el archivo de script de configuración. El proceso de instalación creará una biblioteca separada, libqt-mt, por lo que para admitir la programación multiproceso, debe vincular con esta biblioteca (la opción de vínculo es -lqt-mt) en lugar de la biblioteca Qt habitual (-lqt).
Además, no importa qué plataforma sea, debe definir la macro QT_THREAD_SUPPORT (es decir, agregar la opción de compilación -DQT_THREAD_SUPPORT) al agregar soporte para subprocesos. En los sistemas operativos Windows, esto generalmente se logra agregando una opción al archivo qconfig.h. En los sistemas Unix, normalmente se agrega al archivo Makefile correspondiente.
2. Clases de subprocesos en Qt
La clase más importante relacionada con los subprocesos en el sistema Qt es, por supuesto, la clase QThread, que proporciona la capacidad de crear un nuevo subproceso y controlarlo. ejecutando varios métodos. El hilo comienza la ejecución a través de la función sobrecargada QThread::run(), que es muy similar a la clase de hilo en el lenguaje Java. En el sistema Qt, siempre hay un hilo de eventos principal de la GUI en ejecución. Este hilo principal obtiene eventos del sistema de ventanas y los distribuye a varios componentes para su procesamiento. También hay un método en la clase QThread para enviar eventos a un objeto desde un hilo de eventos no principal, que es el método QThread::postEvent(). Este método proporciona un proceso de envío de eventos seguro para subprocesos en Qt. El evento enviado se coloca en una cola y luego se activa el hilo de eventos principal de la GUI y envía el evento al objeto correspondiente. Este proceso es el mismo que el proceso de procesamiento de eventos del sistema de ventanas general. Vale la pena señalar que cuando se llama al controlador de eventos, se llama en el hilo del evento principal, no en el hilo que llamó al método QThread::postEvent. Por ejemplo, el usuario puede forzar a otro hilo a repintar un área específica de un hilo:
QWidget *mywidget;
QThread::postEvent(mywidget, new QPaintEvent(QRect(0, 0,100,100) ));
Sin embargo, una sola clase de subproceso no es suficiente para escribir un programa que admita subprocesos múltiples, dos subprocesos diferentes deben implementar un acceso mutuamente exclusivo a los datos. Qt También se proporciona la clase QMutex. Cuando un subproceso accede a datos críticos, necesita bloquearse. En este momento, otros subprocesos no pueden bloquear los datos críticos al mismo tiempo hasta que el subproceso anterior los libere. De esta forma, se pueden lograr operaciones atómicas sobre datos críticos.
Además, se necesita algún mecanismo para permitir que los subprocesos en estado de espera se despierten en circunstancias específicas. La clase QWaitCondition proporciona esta funcionalidad. Cuando ocurre un evento específico, QWaitCondition activará todos los subprocesos que esperan el evento o activará cualquier subproceso seleccionado.
3. Aplicación de eventos definidos por el usuario en programación multiproceso
En el sistema Qt, se definen muchos tipos de eventos, como eventos de temporizador, eventos de movimiento del mouse y eventos de teclado. eventos, eventos de control de ventanas, etc. Por lo general, los eventos provienen del sistema de ventanas subyacente. La función de bucle de eventos principal de Qt obtiene estos eventos de la cola de eventos del sistema, los convierte en QEvents y luego los pasa a los objetos QObjects correspondientes.
Además, para satisfacer las necesidades del usuario, el sistema Qt también proporciona una clase QCustomEvent para eventos definidos por el usuario. Estos eventos personalizados pueden usar QThread::postEvent() o QApplication::postEvent(). enviados a varios controles u otras instancias de QObject, y las subclases de la clase QWidget pueden recibir fácilmente estos eventos personalizados a través de la función de manejo de eventos QWidget::customEvent(). Cabe señalar que los objetos QCustomEvent se crean con una identificación de tipo para definir el tipo de evento. Para evitar conflictos con los tipos de eventos definidos por el sistema Qt, el valor de identificación debe ser mayor que "" proporcionado en el tipo de enumeración QEvent. ::Tipo. Valor de usuario.
En el siguiente ejemplo, se muestra cómo utilizar clases de eventos definidas por el usuario en programación multiproceso.
La clase UserEvent es una clase de evento definida por el usuario y su identificador de evento es 3467Array8, lo que obviamente no entra en conflicto con los tipos de eventos definidos por el sistema.
clase UserEvent: public QCustomEvent //Clase de evento definida por el usuario
{
public:
UserEvent(QString s): QCustomEvent (3467Array8), sz(s) { ; }
QString str() const { return sz }
privado:
QString
QString sz;
p>
};
La clase UserThread es una subclase heredada de la clase QThread además de definir variables relevantes y funciones de control de subprocesos. , lo más importante en esta clase es definir el inicio del hilo. Función UserThread::run(), en la que se crea un evento definido por el usuario UserEvent y la función postEvent de la clase QThread se utiliza para enviar el evento. al objeto receptor correspondiente.
clase UserThread: public QThread //Clase de subproceso definida por el usuario
{
público:
UserThread(QObject *r, QMutex *m, QWaitCondition *c);
QObject *receiver;
}
void UserThread::run() //Función de inicio de clase de subproceso, en este Se crea un evento definido por el usuario en la función
{UserEvent *re = new UserEvent(resultstring);
QThread::postEvent(receiver, re
<); p >}La clase UserWidget es una subclase de la clase QWidget definida por el usuario para recibir eventos personalizados. Esta clase utiliza la función slotGo() para crear un nuevo hilo recv (clase UserThread). Cuando el evento personalizado correspondiente (es decir, la identificación es 3467Array8), use la función customEvent para procesar el evento.
void UserWidget::slotGo() //Función miembro del control definido por el usuario
{ mutex.lock()
if (! recv)
p>recv = new UserThread(this, &mutex, &condition);
recv->start();
mutex.unlock();
}
void UserWidget::customEvent(QCustomEvent *e) //Función de procesamiento de eventos definida por el usuario
{ if (e->type()==3467Array8)
{
UserEvent *re = (UserEvent *) e;
newstring = re->str();
}
}
En este ejemplo, se crea un nuevo hilo UserThread en el objeto UserWidget. El usuario puede usar este hilo para implementar algún procesamiento periódico (como recibir mensajes de la capa inferior, etc.). Una vez que envía un evento definido por el usuario en condiciones específicas, cuando el objeto UserWidget recibe el evento, puede manejarlo en consecuencia según sea necesario. En circunstancias normales, el objeto UserWidget puede realizar cierto procesamiento de rutina normalmente sin verse afectado por él. en absoluto.
4. Utilice el mecanismo de temporizador para implementar la programación multiproceso.
Para evitar los problemas causados por la programación multiproceso en el sistema Qt, también puede utilizar el mecanismo de temporizador. proporcionado en el sistema para implementar una función similar. El mecanismo del temporizador serializa eventos concurrentes, simplificando el procesamiento de eventos concurrentes y evitando así problemas de seguridad para subprocesos.
En el siguiente ejemplo, hay varios objetos que necesitan recibir mensajes de la capa inferior al mismo tiempo (a través de mecanismos de comunicación entre procesos como Socket y FIFO), y los mensajes se reciben de forma aleatoria y Se requiere una GUI. El hilo principal es específicamente responsable de recibir mensajes. Al recibir un mensaje, el hilo principal inicializa el objeto correspondiente para comenzar a procesarlo y regresa al mismo tiempo. De esta manera, el hilo principal siempre puede actualizar la visualización de la interfaz y recibir mensajes del mundo exterior para controlar múltiples objetos al mismo tiempo. Por otro lado, cada objeto debe ser notificado al hilo principal de la GUI después de procesar el mensaje. Para este problema, puede usar el método de evento definido por el usuario en la Sección 3 para instalar un filtro de eventos en el hilo principal para capturar eventos personalizados enviados desde cada objeto y luego enviar una señal para llamar a un filtro de eventos en la ranura del hilo principal. función.
Además, también puedes utilizar el mecanismo de temporizador en Qt para implementar funciones similares sin preocuparte por problemas de seguridad para subprocesos. La siguiente es la parte del código relevante:
El temporizador se crea e inicia en la clase de servidor definida por el usuario, y la función de conexión se utiliza para asociar el tiempo de espera del temporizador con la lectura de datos del archivo del dispositivo:
Servidor:: Servidor(QWidget *parent): QWidget(parent)
{
readTimer = new QTimer(this); //Crea e inicia el temporizador
connect(readTimer, SIGNAL(timeout()), this, SLOT(slotReadFile())); //Llama a la función slotReadFile para leer el archivo cada vez que se agote el tiempo de espera
readTimer-> start( 100);
}
La función slotReadFile es responsable de leer los datos del archivo cuando el temporizador se agota y luego reiniciarlo:
int Server: :slotReadFile() // Función de lectura y procesamiento de mensajes
{
readTimer->stop() // Detiene temporalmente el tiempo del temporizador
ret = read(file, buf); //Leer archivo
if(ret == NULL)
{ readTimer->start(100); mensaje, Reiniciar el cronómetro
return(-1);
}
else
Distribuir el mensaje a cada uno según el contenido en buf Procesamiento de objetos correspondientes...;
readTimer->start(100); //Reiniciar el temporizador
}
En este programa, use The El archivo de dispositivo especificado por el usuario se lee periódicamente de forma circular y la información se envía a cada objeto correspondiente en función del contenido de los datos leídos. Los usuarios pueden crear una clase de Servidor en su propio hilo principal de GUI para ayudar a implementar el proceso de recepción de mensajes subyacente, mientras siguen manejando problemas como la visualización de la interfaz. Cuando cada objeto ha completado el procesamiento, el proceso de lectura periódica del archivo del dispositivo subyacente continúa reiniciando el temporizador. Por supuesto, este método es adecuado para situaciones en las que el tiempo de procesamiento de eventos de cada objeto es corto y la frecuencia de los mensajes enviados por el dispositivo subyacente es relativamente lenta. En este caso, el método anterior puede satisfacer completamente las necesidades de los usuarios y al mismo tiempo evitar lidiar con algunos problemas complejos relacionados con la concurrencia de subprocesos.
Por supuesto, el uso del mecanismo de temporizador para implementar la programación multiproceso tiene ciertas limitaciones en algunos aspectos. Cómo implementar la programación multiproceso y cómo escribir código más eficiente aún requiere mayor desarrollo por parte de los desarrolladores. conversar.