Está en la página 1de 18

{Genera cinco nmeros del 1 al 100 sin repetir ni cero}

program numeros;
uses
crt;
var
arraynume : array[1..5] of integer;
t, p, nu : integer;
sis, fin : boolean;
begin
clrscr;
fin := false;
p := 1;
sis := false;
randomize;
repeat
nu := random(101);
for t := 1 to p do
if (arraynume[t] = nu) or ( nu = 0) then
sis := true;
if sis = false then
begin
arraynume[p] := nu;
p := p + 1;
if p > 5 then
fin := true;
end;
sis := false;
until fin = true;
for t := 1 to 5 do
write(' ',arraynume[t]);
readkey;
end.

uses crt;
var a:integer;
{*************************************************************************}
function num_aleatorio(maximo:word):longint;
begin
{ randomize: obtiene una semilla aleatoria NUEVA del reloj del sistema
si no usasemos randomize ,cada vez que usemos random x veces
terminaria por repetirse la secuencia aleatoria ,
no siendo tan aleatoria
}
randomize;
{ Random: genera un numero aleatorio entre 0 y maximo , puede ser 0 pero
nunca llega a ser maximo , tener en cuenta, }

num_aleatorio:=random(maximo);
end;
{*************************************************************************}
begin
clrscr;
{ Nota : - Random - genera un numero aleatorio entre 0 y maximo , puede
ser 0 pero nunca llega a ser maximo .
Aqu simulamos la secuencia de tirar un DADO x veces , observa como
anulamos que salga un cero y forzamos a que salga un 6 , simplemente
sumando un 1 al nmero generado }

Writeln('Deja Pulsado ESC para salir');

repeat
for a:=1 to 10 do begin
gotoxy(1,5);
write('Tirada ',a:4,' Valor:',num_aleatorio(6)+1:1,' ');
delay(3500);
end;
until port[$60]= 1;

end.

las rutinas cursor_on y off estan por estetica , para que no parpadee el cursor
de la pantalla.
aqu te muestro dos formas diferentes de controlar el tiempo mediante gettime
de pascal , y mediante BIOS .

Saludos y a estudiar un poquito mas.

______________________________________________________________

uses crt,windos; { gettime necesita la unidad Windos}


var hora,minuto,segundo,sec100:word;
tiempo_transcurrido:longint;
tiempo_inicial:longint;
tiempo_actual:longint;
dato:real;
{***************************************************************************}
procedure cursor_off;assembler;
{ * apaga el cursor del BIOS (no mouse) }
asm
mov ah,01
mov ch,20h
mov cl,12
int 10h
end;
procedure cursor_on;assembler;
{ * restablece el cursor por defecto BIOS }
asm
mov AH,01
mov CH,11
mov CL,12
int 10h
end;
{***************************************************************************}

begin
clrscr;
cursor_off;
gettime(hora,minuto,segundo,sec100);

{
en las siguientes posiciones de memoria que guarda el contador de hora
del dia BIOS se almacena el tiempo trancurrido que lleva encendido el
ordenador y el tiempo se mide en 18 avos de segundo, mas exactamente
18.2 avos de segundo

mem[$40:$006c]
mem[$40:$006d]
mem[$40:$006e]

mirar tambien la funcion 1ah del DOS


}

tiempo_inicial:=(mem[$40:$006c]+(mem[$40:$006d]*256)+
(mem[$40:$006e]*65536));
repeat
gettime(hora,minuto,segundo,sec100);
gotoxy(1,2) ;
write('Hora:',hora,' Min:',minuto:2,' Seg:',segundo:2,' Dec: ',sec100:2,' ');

tiempo_transcurrido:=(mem[$40:$006c]+(mem[$40:$006d]*256)+
(mem[$40:$006e]*65536));
tiempo_actual:=tiempo_transcurrido-tiempo_inicial;
gotoxy(1,4) ;
{dato:=(tiempo_actual /(18.2) );{}
dato:= tiempo_actual / 18.2;
writeln(' Transcurren ',dato:4:0 ,' segundos ');
writeln;
writeln(' Tiempo sin Redondear ',dato:4:5);

until port[$60]=1;

cursor_on;

end.

Algo un poco mas sencillo.

program timer;
uses crt,dos;

const
wx1 = 35;
wy1 = 12;
wx2 = 45;
wy2 = 14;

var
h,m,s,sent,hb,mb,sb,sentb : word;
i : byte;

function salir : boolean; {esta funcion espera hasta que presions la tcla
ESCAPE}
var
c : char;
begin
salir:=false;
if keypressed then
begin
c:=readkey;
if (ord(c)=27) then salir:=true;
end;
end; {fin de salir}

begin {Prog Principal}


clrscr;
write('presione enter para iniciar');
readln;
write('presione escape para salir');
window(wx1,wy1,wx2,wy2);
gettime(hb,mb,sb,sentb);
repeat
GetTime(h,m,s,sent); {con esto solicitas la hora interna de la pc}
gotoxy(2,2);Write(h:2,':',m:2,':',s:2);
until (salir);
window(1,1,80,25);
clrscr;
h:=h-hb;
m:=m-mb;
s:=s-sb;
write('Resultado >> ',h:2,':',m:2,':',s:2,' presione enter para salir');
readln;
end. {Fin Principal}

Suerte.

Descripcin general
En esta pgina se tratar de explicar cmo escribir y depurar una aplicacin multihilo con
Free Pascal y Lazarus.

Una aplicacin multihilo es aquella que crea dos o ms hilos de ejecucin que trabajan al
mismo tiempo.

Si eres nuevo en programacin multihilo, por favor lee el apartado "Necesito multihilo?"
para saberlo realmente. Puedes ahorrarte un montn de dolores de cabeza.

Uno de los hilos ser el Hilo Principal, este es el que crea el Sistema Operativo al
arrancar la aplicacin.

El Hilo Principal debe ser el nico hilo que actualice los componentes de la interfaz con
el usuario, en caso contrario la aplicacin dejar de funcionar.

La idea principal es que la aplicacin puede hacer alguna tarea en el fondo (en un
segundo hilo), mientras que el usuario puede seguir trabajando (con el hilo principal).

Otro uso de los hilos es para tener una aplicacin que responda mejor. Si crea una
aplicacin, y cuando el usuario pulsa un botn, la aplicacin inicia un proceso (un gran
trabajo) ... y durante el proceso, la pantalla deja de responder, el usuario tendr la sensacin
de que la aplicacin est muerta, lo cual no es agradable. Si la tarea pesada se ejecuta en un
segundo hilo, la aplicacin puede seguir respondiendo (o casi) como si estuviera
desocupada. En este caso es una buena idea, antes de empezar el hilo, desactivar los
botones de la ventana para evitar que el usuario inicie de ms de un hilo para ese trabajo.

Otro uso ms, es crear un servidor capaz de responder a muchos clientes al mismo
tiempo.

Necesitas mltiples hilos?


Si eres novato en esto de los mltiples hilos y lo nico que deseas es hacer que tu
aplicacin responda antes, mientras realiza un trabajo muy pesado, es posible que esto no
sea lo que buscas.

Las aplicaciones con mltiples hilos de ejecucin son siempre ms difciles de depurar y
resultan ms complejas. Y muchas veces es innecesario, con un solo hilo es suficiente. Se
puede dividir la tarea pesada en varios bloques ms pequeos, y usar en el lugar adecuado
Application.ProcessMessages, que permite a la LCL gestionar los mensajes en espera y
continuar despus con nuestro cdigo. La idea es realizar una parte del trabajo, llamar a
Application.ProcessMessages para comprobar si el usuario hace clic en Cancelar o en algn
otro sitio o si es necesario repintar la ventana, y luego continuaremos con la siguiente parte
del trabajo, llamaremos de nuevo a Application.ProcessMessages y as sucesivamente.

Por ejemplo: Leer un fichero muy grande y procesarlo.

Ver ejemplo en ${LazarusDir}/examples/multithreading/singlethreadingexample1.lpi.

La programacin con mltiples hilos es necesaria nicamente para

Bloqueo de enlaces, como en las comunicaciones de red

Utilizar mltiples procesadores a la vez

Llamadas a libreras y algoritmos que no pueden ser divididos en partes ms


pequeas

La clase TThread
El ejemplo que utilizaremos se puede encontrar en el directorio $
{LazarusDir}/examples/multithreading/.

La manera ms fcil de crear una aplicacin de mltiples hilos es utilizar la clase


TThread. Esta clase permite la creacin de un hilo adicional, junto con el hilo principal, de
una manera sencilla.

Por lo general, slo tienen que sobrescribir dos mtodos: el constructor Create y el
mtodo Execute.
En el constructor hay que preparar el hilo para su ejecucin. Hay que dar el valor inicial a
las variables y propiedades que lo precisen. El constructor original de TThread requiere un
parmetro llamado Suspended. Como es de esperar, si Suspended es verdadero (true) se
evitar que el hilo de comience automticamente tras la creacin. Si el valor del parmetro
Suspended es 'false' el hilo se ejecutar inmediatamente despus de la creacin.

Si el hilo se crea suspendido, entonces slo se ejecutar despus de llamar al mtodo


Resume.

Desde la versin 2.0.1 de PFC TThread.Create tiene adems un parmetro implcito


para el tamao de la pila (Stack), con lo que se puede modificar el tamao de la pila de cada
hilo creado si ello es preciso. Un buen ejemplo es la recursin con alto anidamiento de
llamadas. Si no se especifica el tamao de la pila en el parmetro, se utilizar el valor por
defecto del SO.

En el mtodo Execute sobrescrito se pone el cdigo que deseamos que ejecute el hilo.

La clase TThread tiene una propiedad importante:


Terminated : boolean;

Si el hilo tiene un bucle (y esto es lo corriente) este debe terminar cuando Terminated es
cierto (true), el estado por defecto es 'false'. Por tanto es preciso comprobar el valor de
Terminated, y si es 'true' salir del mtodo Execute tan pronto y limpiamente como sea
posible.

Hay que tener en cuenta que el mtodo Terminate no hace nada por defecto: el mtodo
Execute debe realizar explcitamente todas las tareas necesarias para finalizar el trabajo.

Como hemos explicado antes, el hilo no debe interactuar con los componentes visibles.
Para mostrar algo al usuario hay que hacerlo en el hilo principal. Para hacer esto, TThread
tiene un mtodo llamado Synchronize, el cual recibe un parmetro, que consiste en un
mtodo de nuestra clase, que a su vez no tiene parmetros. Cuando se llama a este mtodo a
travs de Synchronize(@MyMethod), la ejecucin del hilo se detendr, el cdigo de
MyMethod se ejecutar en el hilo principal y, a continuacin se reanudar la ejecucin del
hilo.

El cmo se ejecuta Synchronize depende de la plataforma, pero bsicamente hace esto:


Coloca un mensaje en la cola de mensajes principal y 'se va a dormir'. En su momento, el
hilo principal procesa el mensaje y llama a MyMethod. De esta manera se llama MyMethod
sin contexto, es decir que no lo har durante el desplazamiento del ratn o durante el
pintado de la ventana, sino despus. Despus de que el hilo principal ejecuta MyMethod,
despierta al hilo del sueo y procesa el siguiente mensaje. En ese momento el hilo contina
su ejecucin.

Existe otra propiedad importante de TThread: FreeOnTerminate. Si esta propiedad est a


'true', el hilo se liberar automticamente al finalizar la ejecucin del mtodo 'Execute', en
caso contrario hay que liberarlo explcitamente en el cdigo de la aplicacin.
Ejemplo:

Type
TMyThread = class(TThread)
private
fStatusText : string;
procedure ShowStatus;
protected
procedure Execute; override;
public
Constructor Create(CreateSuspended : boolean);
end;

constructor TMyThread.Create(CreateSuspended : boolean);


begin
FreeOnTerminate := True;
inherited Create(CreateSuspended);
end;

procedure TMyThread.ShowStatus;
// // este es el mtodo que se llamar desde el hilo principal con
Synchronize, para acceder a la interfaz.
begin
Form1.Caption := fStatusText;
end;

procedure TMyThread.Execute;
var
newStatus : string;
begin
fStatusText := 'Arrancando TMyThread ...';
Synchronize(@Showstatus);
fStatusText := 'Ejecutando TMyThread...';
while (not Terminated) and ([otra_condicin_requerida]) do
begin
...
//[aqu el cdigo del hilo principal]
...
if NewStatus <> fStatusText then
begin
fStatusText := newStatus;
Synchronize(@Showstatus);
end;
end;
end;

En la aplicacin,

var
MyThread : TMyThread;
begin
MyThread := TMyThread.Create(True); // De esta manera no se inicia
automticamente
...
//[Aqu el cdigo de inicio necesario antes de que comience la
ejecucin de los hilos]
...
MyThread.Resume;
end;

Si se desea hacer la aplicacin ms flexible puede crear un evento para el hilo, de esta
manera su mtodo sincronizado no estar ligado a un formulario o a una clase especfica; y
se podrn usar distintos manipuladores para el evento. Aqu hay un ejemplo:

Type
TShowStatusEvent = procedure(Status: String) of Object;

TMyThread = class(TThread)
private
fStatusText : string;
FOnShowStatus: TShowStatusEvent;
procedure ShowStatus;
protected
procedure Execute; override;
public
Constructor Create(CreateSuspended : boolean);
property OnShowStatus: TShowStatusEvent read FOnShowStatus write
FOnShowStatus;
end;

constructor TMyThread.Create(CreateSuspended : boolean);


begin
FreeOnTerminate := True;
inherited Create(CreateSuspended);
end;

procedure TMyThread.ShowStatus;
// este es el mtodo que se llamar desde el hilo principal con
Synchronize, para acceder a la interfaz
begin
if Assigned(FOnShowStatus) then
begin
FOnShowStatus(fStatusText);
end;
end;

procedure TMyThread.Execute;
var
newStatus : string;
begin
fStatusText := 'Arrancando MyThread...';
Synchronize(@Showstatus);
fStatusText := 'Ejecutando TMyThread...';
while (not Terminated) and ([otra_condicin_requerida]) do
begin
...
//[aqu el cdigo del hilo principal]
...
if NewStatus <> fStatusText then
begin
fStatusText := newStatus;
Synchronize(@Showstatus);
end;
end;
end;

En la aplicacin,

Type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ private declarations }
MyThread: TMyThread;
procedure ShowStatus(Status: string);
public
{ public declarations }
end;

procedure TForm1.FormCreate(Sender: TObject);


begin
inherited;
MyThread := TMyThread.Create(true);
MyThread.OnShowStatus := @ShowStatus;
end;

procedure TForm1.FormDestroy(Sender: TObject);


begin
MyThread.Terminate;
MyThread.Free;
inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);


begin
MyThread.Resume;
end;

procedure TForm1.ShowStatus(Status: string);


begin
Label1.Caption := Status;
end;

Cosas con las que tener especial cuidado


En Windows hay un posible problema cundo se utilizan hilos conjuntamente con el
modificador -Ct (comprobar pila). Por razones no muy claras la comprobacin de la pila se
dispara con todos los TThread.Create si se utiliza el tamao de pila por defecto. La nica
solucin, por el momento, es simplemente no utilizar el modificador -Ct. Tenga en cuenta
que no se producir una excepcin en el hilo principal, pero s en el hilo recin creado. Es
como si el hilo nunca se hubiera iniciado.
Un cdigo adecuado para comprobar si esta u otras excepciones pueden ocurrir en la
creacin del hilo es:

MyThread:=TThread.Create(False);
if Assigned(MyThread.FatalException) then
raise MyThread.FatalException;

Este cdigo asegura que cualquier excepcin que se produzca durante la creacin de un
hilo ser lanzada en su hilo principal.

Unidades necesarias para una aplicacin de mltiples


hilos
No es necesaria ninguna unidad especfica para trabajar en Windows. Sin embargo, con
Linux, MacOSX y FreeBSD, ser necesaria la unidad cthreads que debe ser la primera en
la clusula uses del archivo de programa, .lpr del proyecto.

Por lo tanto, el cdigo de la aplicacin Lazarus debera ser algo as:

program MiProgramaMultiHilo;
{$mode objfpc}{$H+}
uses
{$ifdef unix}
cthreads,
cmem, // el administrador de memoria de c es en algunos sistemas
mucho ms rpido
{$endif}
Interfaces, // this includes the LCL widgetset
Forms
{ add your units here },

Si no lo haces as, y utilizas TThread, obtendrs este error en el arranque:

This binary has no thread support compiled in.


Recompile the application with a thread-driver in the program uses
clause before other units using thread.

Nota:

Si obtienes un error de enlace de "mcount" not found es que se utiliza otra unidad
que contiene cdigo de multiproceso y es necesario agregar la unidad cthreads o
usar enlace inteligente.

Si recibes el error: "Project raised exception class 'RunError(232)" en el


procedimiento SYSTEM_NOTHREADERROR, es que el cdigo requiere multihilo
y hay que aadir la unidad cthreads.

Mltiples hilos en paquetes


Los paquetes que usan mltiples hilos debe aadir el indicador -dUseCThreads en las
opciones del compilador. Abra el paquete, y en el editor, pulsa el botn 'Opciones', en la
pestaa 'Uso' y agrega -dUseCThreads en la opcin 'Personalizado' . Con esta operacin se
definir esta opcin para todos los proyectos y paquetes que utilicen este paquete,
incluyendo el IDE. El IDE y todas las nuevas aplicaciones creadas por el IDE tendrn
entonces el siguiente cdigo en su archivo .lpr :

uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}

Soporte para Sistema de Multiproceso Simtrico (SMP)


Las buenas noticias es que si tu aplicacin funciona correctamente con mltiples hilos de
esta forma, est tambin preparado para SMP.

Depurando aplicaciones de mltiples hilos con Lazarus


La depuracin en Lazarus necesita GDB y est siendo rpidamente ms y ms funcional y
estable. No obstante, hay distribuciones Linux con algunos problemas.

Salida de la depuracin

En una aplicacin de un nico hilo simplemente se escribe en la consola o terminal y las


lneas son escritas en el orden adecuado. En una aplicacin de mltiples hilos las cosas son
ms complicadas. Si dos hilos estn escribiendo, por ejemplo una lnea escrita por un hilo A
antes que una lnea del hilo B, no son necesariamente escritas en ese orden. Puede incluso
suceder que un hilo escriba su salida, mientras el otro hilo est escribiendo una lnea.

La unidad LCLProc contiene varias funciones, para que cada hilo escriba su propio
archivo de registro:

procedure DbgOutThreadLog(const Msg: string); overload;


procedure DebuglnThreadLog(const Msg: string); overload;
procedure DebuglnThreadLog(Args: array of const); overload;
procedure DebuglnThreadLog; overload;

Por ejemplo: En lugar de writeln('Algo de texto ',123); utiliza DebuglnThreadLog(['Algo


de texto ',123]);, de esta manera se aade la lnea 'Algo de texto 123' al archivo
Log<PID>.txt, donde PID es el ID del hilo actual.

Es una buena idea eliminar los archivos de registro despus de cada ejecucin:

rm -f Log* && ./project1

Linux
Si intentas depurar una aplicacin de mltiples hilos en Linux, tendrs un gran problema:
el servidor X se colgar. Si te ocurre esto, simplemente sal de la sesin y crea una nueva
presionando CTRL+ALT+F3. Esto abre una sesin de consola, en ella escribimos sudo
/etc/init.d/gdm restart; con esto rearrancamos el gestor grfico de escritorio, y regresamos
al mismo.

Una forma de resolver este problema en ubuntu x64 es configurar las opciones de
proyecto en depuracin para requerir un fichero de informacin adicional...

Proyecto -> Opciones del Compilador... -> Enlazando -> Depurando: marcar
la opcin Usar archivo externo de smbolos de depuracin para gdb (-Xg).

Si lo anterior no funciona prueba este remedio:

Crear una nueva instancia del servidor X con:

X :1 &

Se abrir un escritorio grfico, y podrs cambiar al escritorio en que estabas trabajando


pulsando CTRL + ALT + F7, y volver al nuevo escritorio con CTRL + ALT + F8 (si esta
combinacin no funciona, prueba con CTRL + ALT + F2 ... sto funciona en Slackware)

Ahora se puede, si se desea, crear una sesin de escritorio en el servidor X recin


arrancado con:

gnome-session --display=:1 &

Luego, en Lazarus, en los parmetros de ejecucin del proyecto en la pestaa Local,


marcar "Usar pantalla" y escribimos ':1'.

Ahora la aplicacin se ejecutar en el segundo servidor X y podremos depurar en el


primero.

Los procedimientos sealados ha sido probados con FreePascal 2.0 y Lazarus 0.9.10 en
Windows y Linux.

En lugar de crear una nueva sesin de X, se puede utilizar Xnest. Xnest es una sesin X
en una ventana. Usndola el servidor X no se bloquea al depurar los hilos, y es mucho ms
fcil depurar sin tener que cambiar entre terminales.

La segunda lnea para ejecutar Xnest es

Xnest :1 -ac

para crear una sesin X en :1, y deshabilitar el control de acceso.


Artefactos Widgetsets
Las interfaces de win32, la de gtk y la de carbon soportan completamente la
programacin con mltiples hilos. Esto significa, que la clase TThread, las secciones
crticas (TCriticalSection) y la sincronizacin (Synchronize) funcionarn.

Pero los subprocesos no son seguros. Esto significa que slo un hilo a la vez puede
acceder a la LCL. Y puesto que el hilo principal no debe esperar a otro hilo, slo al hilo
principal se le permite acceder a la LCL, lo que significa que se ocupar de cualquier cosa
que tenga que ver con TControl, Application o los componentes de la LCL.

Hay algunas funciones de hilo seguro en la LCL. Por ejemplo la mayora de las funciones
de la unidad FileUtil son seguras para subprocesos.

Secciones Crticas
Una seccin crtica es un objeto utilizado para hacer asegurar que cierto cdigo es
ejecutado nicamente por un nico hilo en un momento dado. La seccin critica debe ser
creada e iniciada antes de ser utilizada y debe ser liberada cundo deje de ser necesaria.

Las secciones crticas se utilizan habitualmente de esta forma:

Aadir la unidad SyncObjs.

Declarar la seccin globalmente para todos los hilos que deban acceder a la seccin:

MyCriticalSection: TRTLCriticalSection;

Crear e iniciar la seccin:

InitializeCriticalSection(MyCriticalSection);

Ejecutar algunos hilos. Hacer algo que necesite exclusividad

EnterCriticalSection(MyCriticalSection);
try
// acceder a algunas variables, escribir archivos, enviar algunos
paquetes de red, etc.
finally
LeaveCriticalSection(MyCriticalSection);
end;

Despus de terminar todos los hilos, liberar la seccin crtica:

DeleteCriticalSection(MyCriticalSection);
Como alternativa, puede utilizar un objeto TCriticalSection. La creacin del objeto realiza
el inicio, el mtodo Enter realiza la funcin de EnterCriticalSection, el mtodo Leave
ejecuta LeaveCriticalSection y la destruccin del objeto lleva a cabo la terminacin y
liberacin de recursos.

Por ejemplo: 5 hilos incrementan un contador. Ver $


{LazarusDir}/examples/multithreading/criticalsectionexample1.lpi

Cuidado: Hay dos conjuntos de las 4 funciones mencionadas. Un conjunto de la RTL y


otra de la LCL. Las funciones de la LCL estn definidas en las unidades LCLIntf y
LCLType. Ambos conjuntos trabajan de forma muy parecida. Puede utilizar ambas al
mismo tiempo en su aplicacin, pero no se debe utilizar una funcin de RTL con una
seccin crtica de la LCL, y a la inversa.

Compartiendo variables

Si varios hilos comparten una variable, que es de slo lectura, no hay de que preocuparse,
simplemente lela. Pero si uno o varios hilos cambian el valor de la variable, entonces hay
que asegurar que nicamente un hilo accede a la variable en un momento dado.

Por ejemplo: 5 hilos incrementan un contador. Ver $


{LazarusDir}/examples/multithreading/criticalsectionexample1.lpi

Esperando a otro hilo


En caso de que un hilo A necesite un resultado de otro hilo B, deber esperar hasta que B
termine su ejecucin.

Importante: El hilo principal nunca debe esperar a otro hilo. En lugar de eso utilice
Synchronize (ver ms arriba)

Vase, por ejemplo: ${LazarusDir}/examples/multithreading/waitforexample1.lpi

{ TThreadA }
procedure TThreadA.Execute;
begin
Form1.ThreadB:=TThreadB.Create(false);
WaitForB:=RTLEventCreate; //Crear el evento
while not Application.Terminated do begin
RtlEventWaitFor(WaitForB); // esperar indefinidamente (hasta
que A es despertado por B)
writeln('A: Contador del hilo B='+IntToStr(Form1.ThreadB.Contador));
end;
end;

{ TThreadB }
procedure TThreadB.Execute;
var
i: Integer;
begin
Contador:=0;
while not Application.Terminated do begin
// B: trabajando ...
Sleep(1500);
inc(Contador);
// despertar A
RtlEventSetEvent(Form1.ThreadA.WaitForB);
end;
end;

Rplica (Fork)
Cundo replicamos una aplicacin con mltiples hilos, hay que tener presente que los
hilos han de ser creados y estar en funcionamiento antes de realizar la replica (fork o
fpFork, ya que no pueden ejecutarse en el proceso hijo. Como se indica en el manual de
fork(), el estado de los los hilos que se ejecutan antes de la bifurcacin ser indefinido.

Por lo tanto, hay que tener en cuenta que cualquier hilo iniciado antes de la llamada
(incluidos los de la seccin de inicio), no va a funcionar.

Computacin distribuida
El siguiente gran paso, despus de programar mltiples hilos, es hacer que estos se
ejecuten en mltiples mquinas.

Se puede usar algn paquete de TCP como synapse, lnet o Indy para las
comunicaciones. Esto proporciona la mxima flexibilidad y se usa principalmente
para aplicaciones cliente / servidor con conexiones ligeras.

Se puede usar libreras de envo de mensajes, como MPICH, que se utilizan para
Computacin de Altas Prestaciones (HPC, High Performance Computing) sobre
grupos de ordenadores (clusters).

Hilos externos
Para hacer que el sistema de hilos de Free Pascal funcione correctamente, cada nuevo hilo
FPC tiene que ser iniciado (ms exactamente, las excepciones, el sistema de E/S y el
sistema threadvar por hilo tiene que ser iniciado ya que threadvars y la pila ya estn
trabajando). Esto es totalmente automtico si utilizas BeginThread (o indirectamente
mediante la clase TThread). Sin embargo, si utilizas los hilos que se crearon sin
BeginThread (es decir, hilos externos), trabajo adicional (actualmente) podra ser necesario.
En hilos externos se incluyen los que fueron creados en libreras C externas (.DLL/.so).

Cosas ha considerar cuando se utilizan hilos externos (podra no ser necesario en todas o
en futuras versiones del compilador):
No utilices hilos externos en absoluto; usa hilos FPC. Si puedes conseguir el control
sobre cmo el hilo se crea, crea el hilo por t mismo mediante el uso de
BeginThread.

Si la convencin de llamada no se ajusta (v.g. si tu funcin original del hilo necesita


llamadas con convencin cdecl, pero BeginThread necesita convencin Pascal, crea un
registro, guarda la funcin requerida original de hilos en ella, y llama a esa funcin en la
funcin pascal del hilo:

type
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl;

PCdeclThreadFuncData = ^TCdeclThreadFunc;
TCdeclThreadFuncData = record
Func: TCdeclThreadFunc; //cdecl function
Data: Pointer; //original data
end;

// el hilo pascal llama al funcin cdecl


function C2P_Translator(FuncData: pointer) : ptrint;
var
ThreadData: TCdeclThreadFuncData;
begin
ThreadData := PCdeclThreadFuncData(FuncData)^;
Result := ptrint(ThreadData.Func(ThreadData.Data));
end;

procedure CreatePascalThread;
var
ThreadData: PCdeclThreadFunc;
begin
New(ThreadData);
// esta es la funcin cdecl deseada hilo
ThreadData^.Func := func;
ThreadData^.Data := user_data;
// this creates the Pascal thread
BeginThread(@C2P_Translator, ThreadData );
end;

Inicia el sistema de hilos de FPC mediante la creacin de un hilo vaco. Si no creas


ningn hilo Pascal en tu aplicacin, el sistema de hilos no se puede iniciar (y por
tanto threadvars no funcionar y por lo tanto la pila no funcionar correctamente).

type
tc = class(tthread)
procedure execute;override;
end;

procedure tc.execute;
begin
end;

{ programa principal }
begin
{ Inicia el sistema de hilos }
with tc.create(false) do
begin
waitfor;
free;
end;
{ ... aqu sigue tu cdigo }
end.

(Despus de que el sistema de hilos se inicia, el nucleo de ejecucin establece la variable


del sistema "IsMultiThread" a verdadero, lo que es utilizado por las rutinas FPC para
realizar bloqueos aqu y all. No se debe establecer esta variable de forma manual)

Si por alguna razn esto no te funciona, prueba este cdigo en tu funcin de hilo
externo:

function ExternalThread(param: Pointer): LongInt; stdcall;


var
tm: TThreadManager;
begin
GetThreadManager(tm);
tm.AllocateThreadVars;
InitThread(1000000); // ajustar el tamao de pila inicial aqu

{ hacer algo con hilos aqu ... }

Result:=0;
end;

Identificando hilo externos

A veces incluso no sabes si tienes que tratar con hilos externos (v.g, si alguna librera C
hace una retrollamada -callback-). Esto puede ayudar a analizar esto:

1. Pregunta al S.O. el identificador del subproceso actual al inicio de tu aplicacin

Win32: GetCurrentThreadID();
Darwin: GetThreadID();
Linux: TThreadID(pthread_self);

2. Pregunta por el ID del hilo actual, dentro de la funcin del hilo y compralo con el
resultado del paso 1.

See also

También podría gustarte