Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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 }
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 .
______________________________________________________________
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]
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.
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}
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.
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.
La clase TThread
El ejemplo que utilizaremos se puede encontrar en el directorio $
{LazarusDir}/examples/multithreading/.
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.
En el mtodo Execute sobrescrito se pone el cdigo que deseamos que ejecute el hilo.
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.
Type
TMyThread = class(TThread)
private
fStatusText : string;
procedure ShowStatus;
protected
procedure Execute; override;
public
Constructor Create(CreateSuspended : boolean);
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;
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;
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.
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 },
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.
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Salida de la depuracin
La unidad LCLProc contiene varias funciones, para que cada hilo escriba su propio
archivo de registro:
Es una buena idea eliminar los archivos de registro despus de cada ejecucin:
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).
X :1 &
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.
Xnest :1 -ac
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.
Declarar la seccin globalmente para todos los hilos que deban acceder a la seccin:
MyCriticalSection: TRTLCriticalSection;
InitializeCriticalSection(MyCriticalSection);
EnterCriticalSection(MyCriticalSection);
try
// acceder a algunas variables, escribir archivos, enviar algunos
paquetes de red, etc.
finally
LeaveCriticalSection(MyCriticalSection);
end;
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.
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.
Importante: El hilo principal nunca debe esperar a otro hilo. En lugar de eso utilice
Synchronize (ver ms arriba)
{ 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.
type
TCdeclThreadFunc = function (user_data:Pointer):Pointer;cdecl;
PCdeclThreadFuncData = ^TCdeclThreadFunc;
TCdeclThreadFuncData = record
Func: TCdeclThreadFunc; //cdecl function
Data: Pointer; //original 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;
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.
Si por alguna razn esto no te funciona, prueba este cdigo en tu funcin de hilo
externo:
Result:=0;
end;
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:
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