04 septiembre 2008

Multihilos en xHarbour - Parte VI

En estas primeras entregas sobre los multihilos de xHarbour, he hablado de funciones para crear un nuevo hilo, para sincronizar ejecuciones y para sincronizar accesos a recursos.

Sin embargo, puede suceder que tengamos un hilo al que necesitamos finalizar su ejecución porque no se comporta como esperábamos.
Para eso existen 2 funciones. Stopthread y Killthread.
Además, junto con Stopthread, existe una función para sincronizar la finalización del thread con el thread que lo está finalizando.

Stopthread

Sirve para detener la ejecución de un thread de forma amigable, marcando al thread para que se cierre cuando llegue a uno de los puntos de control.
Los puntos de interrupción son:
  • antes y después de un acceso a disco.
  • antes y después de acceso a TCP/IP.
  • antes y después de llamadas a bloqueos.
  • cada vez que finaliza la ejecución de una función PRG, codebloc o macro.
  • cada 5000 ejecuciones de PCODE dentro de un mismo PRG.
Un thread no puede ser detenido amigablemente cuando esta:
  • esperando por un mutex.
  • esperando por conexión o datos de TCP/IP.
  • mientras ejecuta funciones de C o de PRG compilado en C nativo, que quedan en un loop infinito o muy largo.

Killthread

En general hay que evitar el uso de esta función, siempre hay que tratar de finalizar los threads amigablemente.
Windows en su documentación indica que se evite su uso porque puede dejar incluso registros del kernel en estado incorrecto si al matar el thread el proceso esta ejecutando una función en modo kernel.
También la documentación dice que si el thread tiene un bloqueo, este podría no ser desbloqueado o no ser avisados otros threads de que el mutex esta disponible.

Nos encontramos ante la disyuntiva de matar o no a un thread que no responde.
Si no lo matamos, no podremos finalizar la ejecución.
Si lo matamos, quizás tampoco podamos finalizarla.

Por este motivo, la función Killthread inicialmente marca al thread que se le indica para que se cierre amigablemente, si ya esta marcado entonces sí se lo mata.

En conclusión. Hay que tratar por todos los medios de detener los threads amigablemente, para esto es necesario diseñar el código pensando en incluir puntos de interrupción o de control, si fuera necesario, para ayudar a finalizar el/los threads por las buenas.

Jointhread

Esta función se complementa con Stopthread permitiendo sincronizar la finalización del thread.
Se usa para esperar a que el thread indicado termine su ejecución.

Ejemplo:


Procedure Main()
Local thThread

cls
@8,10 say "Presione una tecla para parar el hilo"
thThread := StartThread( @Work() )
StartThread( @Join(), thThread )

inkey(0)
StopThread( thThread )
WaitForThreads()
Return

Procedure Work()
Local aStat := "\|/-", nStat := 1
do while .t.
  DevOut( "Trabajando ... "+aStat[nStat],,10,10)
  if ++nStat > 4
    nStat := 1
  endif
enddo
return

Procedure Join( thThread )
DevOut("Esperando que finalize el thread de trabajo",,12,10)
JoinThread( thThread )
DevOut("El thread de trabajo ha finalizado correctamente",,12,10)
return

No hay comentarios.: