HILOS DE TRABAJO( worker threads)
MFC distingue entre dos tipos de hilos, de trabajo e interfaz de usuario (UI) hilos. Los hilos de trabajo, estan planeados para ser usados como hilos secundarios que realizan unidades de trabajo sin conexion con la interfaz de usuario. Esto significa tecnicamente que los hilos de trabajo no tienen asociada una ventana, por lo que no tienen bucle de mensajes definido, por lo que no existe una directa interacion entre un hilo de trabajo y el usuario. Los hilos de trabajo son típicamente usados para realizar largos procedimientos que se realizan asincronamente mientras el resto del programa continua interactuando con el usuario. Ejemplos comunes de hilos de trabajo pueden ser recalculaciones de hojas de cálculo y compilacion de programas en un entorno de desarrollo.
Los hilos de trabajo son lanzados del mismo modo que los hilos estandard de Win32 son lanzados- especificando la función que queremos que se ejecute en el hilo (funcion de trabajo??). MFC provee una funcion de ayuda global que ayuda a lanzar los hilos (sobrecargada para hilos de trabajo y de usuario), que para hilos de trabajo es algo asi:CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
Un ejemplo de un simple hilo de trabajo es:Cuando la funcion AfxBeginThread() se llama con el puntero a funcion DoWork() como un parameter, la funcion DoWork() se ejecutara en el hilo independiente que se ha creado para ella.
UINT DoWork(LPVOID pParam)
{
int* count = static_cast(pParam); return 0; }...
for (int i=0; i<*count; i++);
int* i = new int(10000);
CWinThread* pThread = AfxBeginThread(DoWork, i);
Esto significa que el codigo que está corriendo en el hilo actual, se ejecutara simultanemente con el hilo que se ha creado nuevo. Esta es una de las grandes ventajas de la programación multihilo, la habilidad de generar trabajos si interrumpir la ejecución del programa principal o primario. Esta es una particularidad relevante de la programación en Windows (o otros sistemas basados en mensajes) puesto que de realizar procesos pesados sin hilos, perjudicaría severamente la interface de usuario(Que tiene que estar continuamente leyendo mensajes para poder funcionar N.T.).
Hay tambien que recordar, que la programación multithread pese a ser una atractiva solución a muchos problemas, no lo es sin un coste. El mayor coste es usualmente el añadir complejidad a los programas que gestionan múltiples threads, frecuentemente problemas de sincronización.
Ejecutar aplicaciones miltithreads en systemas de un-procesador tambien incurren en problema de rendimiento debido al gasto de recursos relacionados con la gestión de estos nuevos hilos
HILOS DE INTERFAZ DE USUARIO
Una vez que uno se ha derivado una clase CWinThread y sobreescrito InitInstance () para crear o asociar una ventana primaria con el hilo, hay otra versión sobrecargada de AfxBeginThread () que pueden utilizarse para generar hilos de interfaz de usuario:
CWinThread* AfxBeginThread( CRuntimClass* pThreadClass,Notar, que en lugar de pasar una funcion como puntero al primer parámetro, se pasa un puntero a un objeto CRuntimeClass que referencia a la clase que hemos derivado de CWinThread. Este objeto permite a la función AfxBeginThread() crear una nueva instancia ( por la funcion CreateObject() de CRuntimeClass). Se puede acceder a la estructura de clase runtime usando la macro RUNTIME_CLASS() con el nombre de la clase. Un ejemplo de un hilo UI simple definido y generado es:
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
class CUIThread : public CWinThread {
DECLARE_DYNCREATE(CUIThread)
public: // Override InitInstance() only
virtual BOOL InitInstance();
};
IMPLEMENT_DYNCREATE(CUIThread, CWinThread)
BOOL CUIThread::InitInstance()
{
CFrameWnd* wnd = new CFrameWnd;
wnd->Create(NULL, "UI Thread Window");
wnd->ShowWindow(SW_SHOW);
wnd->UpdateWindow();
m_pMainWnd = wnd;
return TRUE; }
CWinThread* pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
Lo que sucede cuando un hilo de la interfaz de usuario es generado, es muy similar a lo que sucede cuando el hilo principal de su aplicación se inicia. La Figura 1 muestra la secuencia de llamadas a funciones CWinThread virtual (que se puede sobreescribir en su clase CWinThread derivada ) hechas por MFC cuando se crea un hilo de la interfaz de usuario.
InitInstance()
|
V
run() ------> onIdle()
|
V
ExitInstance()
Primero, MFC llama a la funcion InitInstance() de nuestra nueva clase derivada de la clase CWinThread. Si resulta true, procede a llamar a la funcion Run() de nuestro objeto, que si no hemos sobreescrito, ejecutara el bucle de mensajes standard y termina cuando recibe el mensaje WM_QUIT. Durante el bucle de mensajes,la función Run() llamará a la función OnIdle() cada vez que detecte tiempo de descanso de proceso (idle-time) en el hilo, cuando Run() retorna, MFC llama al metodo ExitInstance() , dándole la oportunidad de limpiar todos los recursos que nuestro hilo pueda tener asignados.
Podemos sacar ventaja de conocer como son generados los hilo UI, y usar esta idea para diseñar hilos de trabajo. Por ejemplo, podemos derivar una nueva clase de CWinThread para realizar el trabajo que queríamos con el hilo de trabajo dentro delInitInstance(), y entnoce rotornar falso en InitInstance() para indicar a MFC que no deseamos que continue con el bucle de mensajes.
No es necesario usar la MFC funcion AfxBeginThread() cuando crea hilo UI (o worker thread, de este modo). En lugar de esto pude siemplemente generar una instancia de CWinThread (o su derivada) y lanzar el hilo invocando metodo de CWinThread llamado CreateThread(), de esta forma.
BOOL CWinThread::CreateThread(DWORD dwCreateFlags = 0,
UINT nStackSize = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
Un ejemplo de crear una instancia de su CUIThread clase y generar el hilo es:
Esto es esencialmente lo que el AfxBeginThread () hace (como veremos), pero este enfoque nos da más control sobre la forma en que el CWinThread derivado de la clase es instanciada.
CUIThread* pThread = new CUIThread;
pThread->CreateThread();
HilosUI no son comunmente usados como hilos de trabajo en programa MFC. Los hilos UI se usan normalmente en las interfaces de usuario, como la ventana principal, menu, etc., usualmente ocupa el primer hilo de una aplicación. Hilos de trabajo secundarios son lanzandos cuando pesados procesos necesitan hacerse , o chquear e estados asincronos (como monitorizar algun dispositivo), permitiendo al hilo primario(UI) continuar sin parones, y permitiendo a la interface del usuario proceder sin vacilar.
COMO MFC CREA HILOS
Lo primero que debemos de saber de como MFC crea los hilos, es que estan siempre creados con la función _beginthreadex(), Tanto los UI como los de trabajo. El uso de _beginthreadex() (en vez de _beginthread()) dan a MFC mas flexibilidad en como los hilos son creados, incluida la habilidad de poner atributos de seguridad y modificar el estado de ejecución del hilo.
unsigned long _beginthreadex( void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr );
La razón por la que MFC utiliza el _beginthreadex () en lugar de la API de Win32 función CreateThread (), es que evita que las pequeñas fugas de memoria cuando se vayan realizando las llamadas a las bibliotecas de C en tiempo de ejecución.
Lo segundo que necesitamos saber sobre como crea MFC los hilos, es que siempre debemos usar la función CreateThread() de la clase CWinThread para crear los thread que usa MFC(en lugar de invocar el API directamente). La funcion CreateThread puede ser llamada explicitamente on un CWinThread objeto, o implicitamente creado el hilo usando AfxBeginThread().
Si, por ejemplo, queremos generar un hilo llamando a _beginthread() generara un error de assertion tan pronto se haya generado
Para entender como es de crítico que todos los threads usados en MFC tienen que ser llamados usando CWinThread::CreateThread(), podemos tracear los pasos hechos por MFC cuando un nuevo hilo es generado. Lo primero que hace MFC cuando crea un nuevo hilo usando AfxBeginThread (), es crear una nueva instancia de la clase CWinThread (o derivada) . Como lo hace, depende de si es hilo de trabajo o de usuario. Para el hilo de usuario, la forma es la siguiente:
CWinThread* pThread =
new CWinThread(pfnThreadProc, pParam);
Donde pfnThreadProc y pParam son los parametros padados a AfxBeginThread() tal y como se ve arriba.Para hilos UI , usa la class runtime pasada en la creación de la nueva instancia derivada de CWinThread :
CWinThread* pThread = (CWinThread*) pThreadClass->CreateObject();
Una vez MFC tiene una instancia de una clase CWinThread a trabajando, se
procede a llamar CreateThread () sobre ese objeto que es cuando se genera el hilo.
pThread->CreateThread(dwCreateFlags | CREATE_SUSPENDED,
nStackSize, lpSecurityAttrs);
Dentro de CreateThread(), se hace la llamada a _beginthreadex() - pero la función que hace esta llamada, no es la funcion AfxBeginThread(). MFC añade aqui un nivel de indirecicion qui que le permite una interna inicialización se añada antes que la funcion que genera el hilo sea invocada. La forma de lanzar el hilo es:
m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs,
nStackSize, &_AfxThreadEntry, &startup, dwCreateFlags |
CREATE_SUSPENDED,
(UINT*)&m_nThreadID);
4 comentarios:
Increible que no tengas ningun comentario aun en este post. La mejor explicación que he encontrado al respecto.Muy agradecido.
Muchas gracias, sin duda la mejor explicación en español que he visto.
2012 y sigue siendo una de las mejores explicaciones al resecto! gracias por el arduo trabajo!
Permiso... dejo mi agradecimiento por la clara explicación que dejaste!
Publicar un comentario