¿Por qué no puedes hacer otras cosas mientras ejecutas el programa?
1. Pregunta
Escriba un programa de un solo subproceso que requiera mucho tiempo:
Cree una nueva aplicación basada en diálogo SingleThread, en el cuadro de diálogo principal IDD_SINGLETHREAD_DIALOG Agregar un botón con el ID IDC_SLEEP_SIX_SECOND y el título "Retraso de 6 segundos". Agregue la función de respuesta del botón. El código es el siguiente:
void CSingleThreadDlg::OnSleepSixSecond()
{<. / p>
Sleep(6000); //Retraso de 6 segundos
}
Compile y ejecute la aplicación, haga clic en el botón "Retraso de 6 segundos" y estará Se descubrió que durante estos 6 segundos el programa se "congelaba" y ya no respondía a otros mensajes. Para manejar mejor esta operación que requiere mucho tiempo, necesitamos aprender: programación multiproceso.
2. Descripción general de subprocesos múltiples
Los procesos y los subprocesos son conceptos del sistema operativo. Un proceso es una instancia de ejecución de una aplicación. Cada proceso se compone de espacio de direcciones virtuales privadas, código, datos y otros recursos del sistema. Los recursos creados durante el proceso en ejecución se destruyen cuando el proceso finaliza. el proceso termina.
Un hilo es una unidad de ejecución dentro de un proceso. Después de que el sistema crea un proceso, en realidad inicia el hilo de ejecución principal que ejecuta el proceso. El hilo de ejecución principal proporciona el punto de partida del programa al sistema Windows en forma de una dirección de función, como la función principal o WinMain. Cuando finaliza el hilo de ejecución principal, el proceso también finaliza.
Cada proceso tiene al menos un hilo de ejecución principal, que no necesita ser creado activamente por el usuario, pero lo crea automáticamente el sistema. Los usuarios crean otros subprocesos en la aplicación según sea necesario y varios subprocesos se ejecutan simultáneamente en el mismo proceso. Todos los subprocesos de un proceso están en el espacio de direcciones virtuales del proceso y utilizan estos espacios de direcciones virtuales, variables globales y recursos del sistema simultáneamente, por lo que la comunicación entre subprocesos es muy conveniente y la tecnología de subprocesos múltiples se utiliza ampliamente.
El subproceso múltiple puede lograr un procesamiento paralelo, evitando que una determinada tarea ocupe tiempo de CPU durante mucho tiempo. Una cosa a tener en cuenta es que la mayoría de las computadoras actualmente tienen un solo procesador (CPU). Para ejecutar todos estos subprocesos, el sistema operativo programa algo de tiempo de CPU para cada subproceso independiente. El sistema operativo proporciona intervalos de tiempo a los subprocesos de forma rotativa. . Esto da la ilusión de que todos estos hilos se están ejecutando al mismo tiempo. Se puede ver que si dos subprocesos muy activos compiten por el control de la CPU, consumirán muchos recursos de la CPU al cambiar de subproceso, lo que en realidad reducirá el rendimiento del sistema. Esto debe tenerse en cuenta al realizar programación multiproceso.
Las funciones del SDK de Win32 admiten programación multiproceso y proporcionan diversas operaciones de sincronización, exclusión mutua y secciones críticas basadas en los principios del sistema operativo. En Visual C 6.0, la programación multiproceso también se implementa utilizando la biblioteca de clases MFC, lo que hace que la programación multiproceso sea más conveniente.
3. La API de Win32 admite programación multiproceso
Win32 proporciona una serie de funciones API para completar la creación, suspensión, recuperación, terminación y comunicación de subprocesos. Algunas de las funciones importantes se seleccionarán para su descripción a continuación.
1. HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);
Esta función crea un nuevo hilo en el espacio de proceso del proceso que llama y devuelve el identificador del hilo creado, con cada parámetro El la descripción es la siguiente:
lpThreadAttributes: un puntero a una estructura SECURITY_ATTRIBUTES, que determina los atributos de seguridad del hilo y generalmente se establece en NULL
dwStackSize: especifica la profundidad de la pila; del hilo, generalmente Todos se establecen en 0;
lpStartAddress: indica la dirección de la función donde se encuentra el código cuando el nuevo hilo comienza a ejecutarse, es decir, la dirección inicial del hilo. La situación general es (LPTHREAD_START_ROUTINE)ThreadFunc, ThreadFunc es el nombre de la función del subproceso
lpParameter: especifica los parámetros de 32 bits transmitidos al subproceso cuando se ejecuta el subproceso, es decir, los parámetros del subproceso; función de hilo;
dwCreationFlags: indicador adicional para controlar la creación de hilos, puede tomar dos valores. Si este parámetro es 0, el hilo comenzará a ejecutarse inmediatamente después de su creación; si este parámetro es CREATE_SUSPENDED, después de que el sistema genere el hilo, el hilo estará en un estado suspendido y no se ejecutará inmediatamente hasta que se llame a la función ResumeThread;
lpThreadId: Este parámetro devuelve el ID del hilo creado; si la creación es exitosa, devuelve el identificador del hilo, de lo contrario devuelve NULL.
2. DWORD SuspendThread(HANDLE hThread);
Esta función se utiliza para suspender el hilo especificado. Si la función se ejecuta con éxito, la ejecución del hilo finaliza.
3. DWORD ResumeThread(HANDLE hThread);
Esta función se utiliza para finalizar el estado suspendido del hilo y ejecutar el hilo.
4. VOID ExitThread(DWORD dwExitCode);
Esta función se utiliza para finalizar la ejecución del hilo y se llama principalmente en la función de ejecución del hilo. El parámetro dwExitCode se utiliza para establecer el código de salida del hilo.
5. BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
Generalmente, una vez finalizado el hilo, la función del hilo regresa normalmente, pero la aplicación puede llamar a TerminateThread para terminar por la fuerza un determinado hilo. hilo. ejecución del hilo. El significado de cada parámetro es el siguiente:
hThread: el identificador del hilo que se va a terminar;
dwExitCode: se utiliza para especificar el código de salida del hilo.
Usar TerminateThread() para finalizar la ejecución de un subproceso no es seguro y puede causar inestabilidad en el sistema; aunque esta función finaliza inmediatamente la ejecución del subproceso, no libera los recursos ocupados por el subproceso. Por lo tanto, generalmente no se recomienda utilizar esta función.
6. BOOL PostThreadMessage(DWORD idThread,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);Esta función coloca un mensaje en la cola de mensajes del hilo especificado y regresa sin esperar a que el hilo procese el mensaje.
idThread: ID del hilo que recibirá el mensaje
Msg: especifica el mensaje utilizado para enviar
wParam: parámetro de palabra relacionado con el mensaje; ;
lParam: parámetro largo relacionado con el mensaje
Al llamar a esta función, si el hilo que está a punto de recibir el mensaje no crea un bucle de mensaje, la función fallará. para ejecutar.
IV.Rutinas de programación multiproceso API Win32
Rutina 1 MultiThread1
Cree un proyecto basado en diálogo MultiThread1 y agregue dos cuadros de diálogo IDD_MULTITHREAD1_DIALOG Un botón y un cuadro de edición Los ID de los dos botones son IDC_START e IDC_STOP. Los títulos son "Inicio" y "Detener" respectivamente. El atributo de IDC_STOP se selecciona como Deshabilitado; el ID del cuadro de edición es IDC_TIME. Solo lectura; p>
Agregue la declaración de función de subproceso en el archivo MultiThread1Dlg.h: void ThreadFunc();
Tenga en cuenta que la declaración de función de subproceso debe estar fuera de la clase CMultiThread1Dlg. Agregue variables protegidas dentro de la clase CMultiThread1Dlg: HANDLE hThread; DWORD ThreadID representa respectivamente el identificador y el ID del hilo.
Agregue la variable global m_bRun en el archivo MultiThread1Dlg.cpp: volátil BOOL m_bRun representa si el hilo se está ejecutando.
Debe tener en cuenta que la variable global m_bRun usa el modificador volátil. La función del modificador volátil es decirle al compilador que no es necesario realizar ninguna optimización en la variable, es decir, no hay necesidad de realizar ninguna optimización. Es necesario ponerlo en un registro y este valor se puede cambiar externamente. Volátil es un modificador muy importante para las variables globales a las que hacen referencia varios subprocesos.
Escribe la función del hilo:
void ThreadFunc()
{
CTime time;
CString strTime
m_bRun=TRUE;
mientras(m_bRun)
{
time=CTime::GetCurrentTime();
strTime=time.Format("H:M:S");
:SetDlgItemText(AfxGetMainWnd()-gt;m_hWnd, IDC_TIME, strTime);
Dormir (1000);
}
}
Esta función de hilo no tiene parámetros y no devuelve un valor de función. Mientras m_bRun sea TRUE, el hilo seguirá ejecutándose.
Haga doble clic en el botón IDC_START para completar la función de mensaje del botón: void CMultiThread1Dlg::OnStart()
{
// TODO: Agregue su controle el código del controlador de notificaciones aquí
hThread=CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc,
NULL, p>
0,
&ThreadID);
GetDlgItem(IDC_START)-gt;EnableWindow(FALSE);
GetDlgItem(IDC_STOP)-gt; EnableWindow( TRUE);
}
Haga doble clic en el botón IDC_STOP para completar la función de mensaje del botón: void CMultiThread1Dlg::OnStop()
{
// TODO: Agregue aquí el código del controlador de notificaciones de control
m_bRun=FALSE;
GetDlgItem(IDC_START)-gt; EnableWindow(TRUE); p>
GetDlgItem (IDC_STOP)-gt; EnableWindow(FALSE);
}
Compile y ejecute esta rutina para experimentar subprocesos múltiples escritos utilizando la API de Win32.
Rutina 2 MultiThread2
Este hilo demuestra cómo transferir un parámetro entero a un hilo y cómo esperar a que un hilo complete el procesamiento.
Cree un proyecto MultiThread2 basado en diálogo, agregue un cuadro de edición y un botón al cuadro de diálogo IDD_MULTITHREAD2_DIALOG, los ID son IDC_COUNT, IDC_START y el título del control del botón es "Inicio" en MultiThread2Dlg; Archivo .h Agregue una declaración de función de subproceso en: void ThreadFunc(int integer); tenga en cuenta que la declaración de función de subproceso debe estar fuera de la clase CMultiThread2Dlg.
Agregue variables protegidas dentro de la clase CMultiThread2Dlg: HANDLE hThread; DWORD ThreadID representa respectivamente el identificador y el ID del hilo. Abra ClassWizard y agregue la variable de tipo int m_nCount al cuadro de edición IDC_COUNT.
Agregue el archivo MultiThread2Dlg.cpp:
void ThreadFunc(int integer)
{
int i;
for(i=0;ilt;integer;i)
{
Bip(200, 50);
Dormir(1000);
}
}
Haga doble clic en el botón IDC_START para completar la función de mensaje del botón:
void CMultiThread2Dlg::OnStart( )
{
UpdateData(TRUE);
int integer=m_nCount;
hThread=CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc,
(VOID*)entero,
0,
amp; ;
GetDlgItem(IDC_START)-gt; EnableWindow(FALSE);
WaitForSingleObject(hThread, INFINITE);
GetDlgItem(IDC_START)-gt; TRUE);
}
Por cierto, la función WaitForSingleObject, su prototipo de función es: DWORD WaitForSingleObject (HANDLE hHandle, DWORD dwMillisegundos
hHandle es); el objeto a monitorear (generalmente
dwMillisegundos es el valor de tiempo de espera establecido por el objeto hHandle, en milisegundos;
Cuando se llama a esta función en un hilo, el hilo se suspende temporalmente, y el sistema monitorea el estado del objeto señalado por hHandle. Si el objeto que el subproceso está esperando se señala dentro de los milisegundos dwMillisegundos suspendidos, la función regresa inmediatamente; si el tiempo de espera ha alcanzado los milisegundos dwMillisegundos, pero el objeto señalado por hHandle aún no ha sido señalado, la función continúa con el retorno habitual. El parámetro dwMillisegundos tiene dos valores con significado especial: 0 e INFINITO. Si es 0, la función regresa inmediatamente; si es INFINITO, el hilo se suspende hasta que se señalice el objeto señalado por hHandle.
El propósito de esta rutina que llama a esta función es esperar hasta que el hilo regrese después de presionar el botón IDC_START y luego restaurar el estado normal del botón IDC_START. Compile y ejecute esta rutina y experimentela cuidadosamente.
Rutina 3 MultiThread3
También es posible pasar una estructura a una función de hilo pasando un parámetro de puntero que apunte a la estructura.
Primero defina una estructura:
typedef struct
{
int firstArgu,
long secondArgu,
…
}myType, *pMyType;
Al crear un hilo, CreateThread(NULL, 0, threadFunc, pMyType,…);
Dentro de la función threadFunc, puedes usar "cast":
int intValue=((pMyType)lpvoid)-gt; firstArgu;
long longValue=((pMyType)lpvoid)-gt; /p>
p>
……
MultiThread3 demostrará cómo pasar un parámetro de puntero que apunta a una estructura.
Cree un proyecto basado en diálogo MultiThread3, agregue un cuadro de edición IDC_MILLISECOND, un botón IDC_START, titulado "Inicio" y una barra de progreso IDC_PROGRESS1 al cuadro de diálogo IDD_MULTITHREAD3_DIALOG, abra ClassWizard y agregue el cuadro de edición IDC_MILLISECOND Int; escriba la variable m_nMilliSecond, agregue la variable de tipo CProgressCtrl m_ctrlProgress a la barra de progreso IDC_PROGRESS1
Agregue una definición de estructura en el archivo MultiThread3Dlg.h:
struct threadInfo
{
UINT nMilliSecond;
CProgressCtrl* pctrlProgress;
};
Declaración de función de subproceso: UINT ThreadFunc(LPVOID lpParam ); p>
Tenga en cuenta que ambos deben estar fuera de la clase CMultiThread3Dlg.
Agregue variables protegidas dentro de la clase CMultiThread3Dlg: HANDLE hThread; DWORD ThreadID representa respectivamente el identificador y el identificador del hilo.
Realice las siguientes operaciones en el archivo MultiThread3Dlg.cpp:
Defina la variable pública threadInfo Info
Haga doble clic en el botón IDC_START y agregue el mensaje correspondiente; función de procesamiento:
void CMultiThread3Dlg::OnStart()
{
// TODO: agregue aquí el código del controlador de notificaciones de control
UpdateData(TRUE);
Info.nMilliSecond=m_nMilliSecond;
Info.pctrlProgress=amp; )ThreadFunc, amp;Info, 0, amp;ThreadID);
/*
GetDlgItem(IDC_START)-gt;EnableWindow(FALSE);
WaitForSingleObject (hThread, INFINITE);
GetDlgItem(IDC_START)-gt; EnableWindow(TRUE);
*/
}
En función BOOL CMultiThread3Dlg :: Agregar declaración a OnInitDialog():
{
……
// TODO: Agregar inicialización adicional aquí
m_ctrlProgress. SetRange(0, 99);
m_nMilliSecond=10;
UpdateData(FALSE);
devuelve VERDADERO; el foco a un control
}
Agregar función de procesamiento de subprocesos:
UINT ThreadFunc(LPVOID lpParam) {
threadInfo* pInfo= (threadInfo* )lpParam;
for(int i=0;ilt;100;i)
{
int nTemp=pInfo-gt;nMilliSecond;
pInfo-gt; pctrlProgress-gt; SetPos(i);
Sueño(nTemp);
}
devuelve 0;
}
Por cierto, si agrega la instrucción /* */ en la función void CMultiThread3Dlg::OnStart(), compila y ejecuta, encontrará que la barra de progreso no se actualizará y el hilo principal no se actualizará. Dejó de reaccionar. ¿Cuál es la razón? Esto se debe a que la función WaitForSingleObject provoca un punto muerto en el subproceso cuando se espera a que finalice el subproceso secundario (ThreadFunc).
Debido a que la función WaitForSingleObject suspenderá el hilo principal (no se procesará ningún mensaje) y el hilo secundario ThreadFunc está configurando la barra de progreso, no detectará el evento de notificación hasta que el hilo principal regrese después de procesar el mensaje de actualización. De esta manera, ambos subprocesos se esperan el uno al otro y se produce un punto muerto, que debe evitarse al programar.
Rutina 4 MultiThread4
Esta rutina prueba el número máximo de subprocesos que se pueden crear en Windows.
Cree un proyecto MultiThread4 basado en diálogo, agregue un botón IDC_TEST y un cuadro de edición IDC_COUNT al cuadro de diálogo IDD_MULTITHREAD4_DIALOG, el título del botón es "Prueba" y la propiedad del cuadro de edición selecciona Solo lectura en MultiThread4Dlg; .cpp Realice las siguientes operaciones en el archivo: agregue la variable pública volátil BOOL m_bRunFlag=TRUE esta variable indica si se pueden seguir creando subprocesos;
Añadir función de subproceso:
DWORD WINAPI threadFunc(LPVOID threadNum)
{
while(m_bRunFlag)
{
Dormir(3000);
}
devuelve 0;
}
Siempre que la variable m_bRunFlag Si es VERDADERO, el hilo sigue ejecutándose.
Haga doble clic en el botón IDC_TEST y agregue su función de mensaje de respuesta:
void CMultiThread4Dlg::OnTest()
{
DWORD threadID;
GetDlgItem(IDC_TEST)-gt; EnableWindow(FALSE);
long nCount=0;
while(m_bRunFlag)
{
if(CreateThread(NULL, 0, threadFunc, NULL, 0, amp; threadID)==NULL)
{
m_bRunFlag=FALSE;
descanso
}
else
{
nCount;
}
//Crear hilos continuamente hasta que no se puedan crear más
m_nCount=nCount;
UpdateData(FALSE);
Sleep(5000);
//Retraso de 5 segundos, esperando a que finalicen todos los hilos creados
GetDlgItem(IDC_TEST)-gt; EnableWindow(TRUE) ;
m_bRunFlag=TRUE;
}
5. Soporte de MFC para programación multiproceso
Hay dos tipos de subprocesos en MFC, que se denominan subproceso de trabajo y subproceso de interfaz de usuario. La principal diferencia entre los dos es que el hilo de trabajo no tiene un bucle de mensajes, mientras que el hilo de la interfaz de usuario tiene su propia cola de mensajes y su propio bucle de mensajes.
Los hilos de trabajo no tienen mecanismo de mensajes y generalmente se utilizan para realizar cálculos en segundo plano y tareas de mantenimiento, como procesos de cálculo prolongados, impresión en segundo plano de impresoras, etc. El subproceso de la interfaz de usuario se utiliza generalmente para procesar la entrada del usuario independientemente de la ejecución de otros subprocesos, responder a eventos y mensajes generados por el usuario y el sistema, etc.
Pero para la programación API de Win32, no hay diferencia entre estos dos subprocesos. Solo necesitan la dirección de inicio del subproceso para iniciar el subproceso y realizar tareas.
En MFC, la función global AfxBeginThread() se usa generalmente para crear e inicializar la ejecución de un subproceso. Esta función tiene dos formas sobrecargadas, que se usan para crear subprocesos de trabajo y subprocesos de interfaz de usuario respectivamente. Los prototipos y parámetros de las dos funciones sobrecargadas se describen a continuación:
(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
nPriority= THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL
PfnThreadProc: apunta a el hilo de trabajo El puntero a la función de ejecución, el prototipo de la función de hilo debe declararse de la siguiente manera:
UINT ExecutingFunction(LPVOID pParam);
Tenga en cuenta que ExecutingFunction() debe devolver un Valor de tipo UINT para Especifica el motivo por el cual finaliza la función. Generalmente, devolver 0 indica una ejecución exitosa.
pParam: Un parámetro de 32 bits pasado a la función del subproceso. La función en ejecución interpretará el valor de alguna manera. Puede ser un valor numérico, o un puntero a una estructura, o incluso puede ignorarse;
nPrioridad: la prioridad del hilo. Si es 0, el hilo tiene la misma prioridad que su hilo principal;
nStackSize: el tamaño de la pila que el hilo se asigna a sí mismo, en bytes. Si nStackSize se establece en 0, la pila del subproceso se establece en el mismo tamaño que la pila del subproceso principal;
dwCreateFlags: si es 0, el subproceso comienza a ejecutarse inmediatamente después de la creación. Si es CREATE_SUSPEND, el hilo se suspende inmediatamente después de la creación
lpSecurityAttrs: el puntero de atributo de seguridad del hilo, generalmente NULL
(2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass; ,
int nPriority=THREAD_PRIORITY_NORMAL,
UINT nStackSize=0,
DWORD dwCreateFlags=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
pThreadClass es un puntero al objeto de clase de tiempo de ejecución de una clase exportada de CWinThread. La clase exportada define el inicio y la salida del hilo de la interfaz de usuario creado. El significado de otros parámetros es el mismo que en el formulario 1; .
El hilo generado usando este prototipo de función también tiene un mecanismo de mensaje. En ejemplos futuros, encontraremos que es casi el mismo que el mecanismo del hilo principal.
A continuación damos una breve descripción de los miembros de datos y funciones comunes de la clase CWinThread.
m_hThread: identificador del hilo actual
m_nThreadID: ID del hilo actual
m_pMainWnd: puntero a la ventana principal de la aplicación
BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,
UINT nStackSize=0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL
dwCreateFlags, The Los parámetros nStackSize y lpSecurityAttrs tienen el mismo significado que los parámetros correspondientes en la función API CreateThread. La función se ejecuta con éxito y devuelve un valor distinto de 0; de lo contrario, devuelve 0.
En circunstancias normales, se llama a AfxBeginThread() para crear e iniciar un hilo una vez, pero también puede crear un hilo mediante un método de dos pasos: primero cree un objeto de la clase CWinThread y luego llame los miembros del objeto Función CreateThread() para iniciar el hilo.
virtual BOOL CWinThread::InitInstance();
Sobrecargue esta función para controlar la inicialización de la instancia del subproceso de la interfaz de usuario. Si la inicialización es exitosa, se devuelve un valor distinto de cero; de lo contrario, se devuelve 0. Los subprocesos de la interfaz de usuario a menudo sobrecargan esta función y los subprocesos de trabajo generalmente no usan InitInstance(). virtual int CWinThread::ExitInstance();
Vuelva a cargar esta función para realizar algunos trabajos de limpieza necesarios antes de que finalice el hilo. Esta función devuelve el código de salida del hilo, 0 indica una ejecución exitosa y se utilizan valores distintos de cero para identificar varios errores. Al igual que la función miembro InitInstance(), esta función solo se aplica al hilo de la interfaz de usuario.
6. Ejemplos de programación multiproceso MFC
En el entorno de programación Visual C 6.0, no solo podemos escribir aplicaciones Win32 de 32 bits estilo C, sino también usar la clase MFC. biblioteca para escribir aplicaciones estilo C, ambas tienen sus pros y sus contras. Las aplicaciones basadas en Win32 tienen códigos de ejecución pequeños y una alta eficiencia de ejecución, pero requieren que los programadores escriban más códigos y administren todos los recursos proporcionados por el sistema al programa, mientras que las aplicaciones basadas en bibliotecas de clases MFC pueden crear rápidamente aplicaciones como La biblioteca proporciona a los programadores; Hay una gran cantidad de clases encapsuladas y Developer Studio proporciona a los programadores algunas herramientas para administrar los programas fuente del usuario. La desventaja es que el código de la biblioteca de clases es muy grande. Debido a las ventajas de utilizar bibliotecas de clases, como velocidad, simplicidad y funciones potentes, Visual C recomienda utilizar bibliotecas de clases MFC para el desarrollo de programas, a menos que existan necesidades especiales.
Sabemos que hay dos tipos de subprocesos en MFC: subprocesos de interfaz de usuario y subprocesos de trabajo. Ilustraremos cada uno con ejemplos.
Utilice la programación de la biblioteca de clases MFC para implementar subprocesos de trabajo
Rutina 5 MultiThread5
Para comparar con la API Win32, utilizamos la programación de la biblioteca de clases MFC para implementar rutina 3 MultiThread3.
Cree un proyecto basado en diálogo MultiThread5, agregue un cuadro de edición IDC_MILLISECOND, un botón IDC_START, titulado "Inicio" y una barra de progreso IDC_PROGRESS1 al cuadro de diálogo IDD_MULTITHREAD5_DIALOG, abra ClassWizard y agregue el cuadro de edición IDC_MILLISECOND Int; escriba la variable m_nMilliSecond, agregue la variable de tipo CProgressCtrl m_ctrlProgress a la barra de progreso IDC_PROGRESS1
Agregue una definición de estructura en el archivo MultiThread5Dlg.h:
struct threadInfo
{
UINT nMilliSecond;
CProgressCtrl* pctrlProgress;
};
Declaración de función de subproceso: UINT ThreadFunc(LPVOID lpParam ); p>
Tenga en cuenta que ambos deben estar fuera de la clase CMultiThread5Dlg.
Agregar una variable protegida dentro de la clase CMultiThread5Dlg: CWinThread* pThread;
Realizar las siguientes operaciones en el archivo MultiThread5Dlg.cpp: definir la variable pública: threadInfo Info
Haga doble clic en el botón IDC_START y agregue la función de procesamiento de mensajes correspondiente:
void CMultiThread5Dlg::OnStart()
{
// TODO: Agregue aquí el código del controlador de notificaciones de control
UpdateData(TRUE);
Info.nMilliSecond=m_nMilliSecond;
Info.pctrlProgress=amp;m_ctrlProgress;
pThread=AfxBeginThread(ThreadFunc,
&Info);
}
Agregar declaración en la función BOOL CMultiThread3Dlg::OnInitDialog(): { p> p>
……
// TODO: Agregue inicialización adicional aquí
m_ctrlProgress.SetRange(0,99);
m_nMilliSecond=10 ;
UpdateData(FALSE);
return TRUE; // devuelve TRUE a menos que establezcas el foco en un control
}
Agregar función de procesamiento de subprocesos:
UINT ThreadFunc(LPVOID lpParam)
{
threadInfo* pInfo=(threadInfo*)lpParam;
for( int i=0; ilt; 100; i )
{
int nTemp=pInfo-gt; nMilliSecond
pInfo-gt; gt; SetPos(i);
Dormir(nTemp);
}
devuelve 0;
}
Utilice la programación de la biblioteca de clases MFC para implementar subprocesos de interfaz de usuario
Pasos para crear subprocesos de interfaz de usuario: utilice ClassWizard para crear una clase derivada de la clase CWinThread (tome la clase CUIThread como ejemplo)
clase CUIThread: public CWinThread
{
DECLARE_DYNCREATE(CUIThread)
protegido:
CUIThread(); utilizado por la creación dinámica
// Atributos
público:
// Operaciones
público:
/ / Anulaciones
// virt generada por ClassWizard
anulaciones de funciones duales
//{{AFX_VIRTUAL(CUIThread)
public:
virtual BOOL InitInstance();
virtual int ExitInstance ();
//}}AFX_VIRTUAL
// Implementación
protegida:
virtual ~CUIThread();
// Funciones de mapa de mensajes generados
//{{AFX_MSG(CUIThread)
// NOTA: ClassWizard agregará y eliminará funciones miembro aquí.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
Funciones sobrecargadas InitInstance() y ExitInstance(). BOOL CUIThread::InitInstance()
{
CFrameWnd* wnd=new CFrameWnd;
wnd-gt;Create(NULL, "UI Thread Window") ;
wnd-gt;ShowWindow(SW_SHOW);
wnd-gt;UpdateWindow();
m_pMainWnd=wnd;
return TRUE;
}
Crear un nuevo hilo de interfaz de usuario void CUIThreadDlg::OnButton1()
{
CUIThread* pThread= new CUIThread();
pThread-gt; CreateThread();
}
Tenga en cuenta los dos puntos siguientes:
A Agregue la declaración al comienzo de UIThreadDlg.cpp: #include "UIThread.h"
B. Cambie las características del constructor de la clase CUIThread() en UIThread.h de protegido a público.
El orden de ejecución del subproceso de la interfaz de usuario es el mismo que el del subproceso principal de la aplicación. Primero, se llama a la función InitInstance() de la clase del subproceso de la interfaz de usuario. Si se devuelve TRUE, continúe llamando. la función Run() del hilo La función de esta función es Ejecutar un bucle de mensajes estándar e interrumpir cuando se recibe el mensaje WM_QUIT. Durante el bucle de mensajes, la función Run() detecta que el hilo está inactivo (sin mensaje) y OnIdle. () también se llamará a la función Run(). Finalmente, la función Run() devuelve , MFC llama a la función ExitInstance() para limpiar recursos.
Puede crear un hilo sin interfaz sino con un bucle de mensajes. Por ejemplo: puede derivar una nueva clase de CWinThread, completar una determinada tarea en la función InitInstance y devolver FALSE, lo que significa que solo InitInstance. Se ejecuta la función Tasks sin ejecutar el bucle de mensajes, puede utilizar este método para completar la función de un hilo de trabajo.