Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Ayuda con Hilos (https://www.clubdelphi.com/foros/showthread.php?t=62965)

Dark_RavenM 23-01-2009 01:48:55

Ayuda con Hilos
 
Hola gente saludos, queria saber si alguien me puede ayudar tengo un problema con hilos, lo que pasa esque quiero que a la hora de ejecutar un query, hacer que mientras trae todos los registros una barra de progreso se este moviendo en lugar de tener el tipico icono del reloj de arena en el cursor, lo que ise es este codigo que viene enseguida pero a la hora de ejecutarlo se ejecuta primero el query luego empieza el hilo y no se por que.

Código Delphi [-]
type
  TBarraProgreso = class(TThread)
  private
  protected
    procedure Execute; override;
  public
  end;


var
  Form1: TForm1;
  hilo:TBarraProgreso;

implementation
var
barra:TProgressBar;cont:integer;

procedure TBarraProgreso.Execute;
begin
   while cont<>1 do
   begin
      barra.StepIt;
      sleep(63);
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  cont:=0;
  hilo:=TBarraProgreso.Create(True);
  barra:=ProgressBar1;
end;

procedure TForm1.Execute1Click(Sender: TObject);
var
Row, Column: Integer;
Col, Row2: integer;
begin
  hilo.Resume;
  if Length(TRim(Comando.Text)) <= 0 then exit;
  screen.Cursor:=crHourGlass;
  memBarre.Lines.Clear;
  memBarre.Lines.Text := Comando.Lines.Text;
  ArmaScript;
     OracleScript.Lines.Clear;
     OracleScript.Output.clear;
     //  OracleScript.Lines := Comando.Lines;
     OracleScript.Lines := memBarre.Lines;
     OracleScript.Execute;
     siglinea(OracleScript.Output.Text);
     StatusBar.Panels[1].Text := '';
     StatusBar.Panels[2].TExt := 'Total Command :'+  IntToStr(OracleScript.CommandIndex) ;
     resultado.Dispatch(ScrollMessage);
     vgsavestate:=0;
  Comando.SelStart:=length(Comando.LineText);
  frmHistory.StgLista.Cells[0,frmHistory.StgLista.RowCount - 1 ] := Comando.Text;
  frmHistory.StgLista.RowCount := frmHistory.StgLista.RowCount + 1;
  frmHistory.StgLista.Row:=frmHistory.StgLista.RowCount-2;
  screen.Cursor:=crDefault;
  vgopen:=false;
end;


si ven tengo hilo.resume antes de ejecutar y armar el script pero se queda estatico hasta despues de que ejecuto el query y me regresa el resultado hasta entonces empieza a moverse la barra de progreso, alguien que sepa que es lo que esta mal o como hacer esto?

xEsk 23-01-2009 06:02:10

Ojo, estas ejecutando codigo "peligroso" dentro de tu hilo. No es nada recomendable acceder a objetos de un TForm directamente dentro de un Thread, ya que la VCL no es thread safe.

Exale un vistazo al metodo "Synchronize". Aqui tienes un ejemplo.

Por otro lado, no enfocas bien el problema... ya que el trabajo "gordo" es el que tiene que estar en el hilo, para que el programa no se quede "congelado"... y no la barra de progreso.

El problema lo tienes pq el script se ejecuta en el hilo principal (el de la aplicacion), y al estar trabajando no puede actualizarse por mucho que tu hilo este modificando la barra de progreso (de forma insegura, como antes te comente).

Saludos.

Kipow 23-01-2009 06:02:53

La ejecucion del Query es una sola instruccion, no vas a poder colocar un progressbar para que te indique el porcentaje de ejecucion del Query.

Dark_RavenM 23-01-2009 17:01:31

Voy a checar lo de Synchronize haber si me ayuda, y Kipow no quiero saber el porcentaje solo quiero que el progressbar se este moviendo infinitamente como si fuera el cursor de reloj de arena para saber que se esta ejecutando y cuando termine ya parar el progressbar

Dark_RavenM 23-01-2009 17:48:29

ok ise este ejemplo de la sincronizacion

Código Delphi [-]
type
  TBarra = class(TThread)
  private
  protected
    procedure detenlabarra;
    procedure Execute; override;
  public
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    RichEdit1: TRichEdit;
    ProgressBar1: TProgressBar;
    OracleSession1: TOracleSession;
    OracleLogon1: TOracleLogon;
    OracleScript1: TOracleScript;
    OracleQuery1: TOracleQuery;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

var
cont:integer;

{$R *.dfm}

procedure TBarra.detenlabarra;
begin
   sleep(2000);
   cont:=1;
end;

procedure TBarra.Execute;
begin
   Synchronize(detenlabarra);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   hilo:TBarra;
begin
   hilo:=TBarra.Create(False);
   while cont<>1 do
   begin
      ProgressBar1.StepIt;
      sleep(125);
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   cont:=0;
end;

pero nunca se detiene, en cambio si le hago asi si

Código Delphi [-]
type
  TBarra = class(TThread)
  private
  protected
    procedure Execute; override;
  public
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    RichEdit1: TRichEdit;
    ProgressBar1: TProgressBar;
    OracleSession1: TOracleSession;
    OracleLogon1: TOracleLogon;
    OracleScript1: TOracleScript;
    OracleQuery1: TOracleQuery;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

var
cont:integer;

{$R *.dfm}

procedure TBarra.Execute;
begin
   sleep(2000);
   cont:=1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   hilo:TBarra;
begin
   hilo:=TBarra.Create(False);
   while cont<>1 do
   begin
      ProgressBar1.StepIt;
      sleep(125);
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   cont:=0;
end;

que podra estar mal?

Chris 23-01-2009 20:22:20

Lo que pasa es que el el primer código, por estar dentro de un synchronize, todo el código de existe en "adentlabarra" se ejecuta a como si estobien en el hilo principal. Por esta razón, cuando llegas a la línea "while ... do" la variable count ya está en 1.

Ahora, cual es la direfencia respecto al segundo código: bien, luego de crear el nuevo hilo, la aplicación sigue su ejecución normal, como si nada la detuviese, pero por el contrario el hilo queda dormido por dos segundos ("sleep(2000)"). Luego de esto, cuando el hilo llega a asignar un valor a la variable cont, la aplicación principal ejecutó "while ... do" hace dos segudos.

No me he explicado bien, es un problema sencillo, pero lo he encontrado difícil de explicar. Sin embargo, comparando ambos código, creo que puedo determinar cual es tu intención:
Código Delphi [-]
var
   hilo:TBarra;
begin
   cont := 0;
   hilo:=TBarra.Create(False);
   while cont<>1 do
   begin
      ProgressBar1.StepIt;
      form1.update;
      sleep(125);
   end;
end;
Utilizando el segundo código, con una ligerisimo modificación, lo que hace el anterior código es hacer mover el progressbar de la aplicación durante 2 segundos.

Saludos.

Dark_RavenM 23-01-2009 21:26:08

ok, ya le cambie, ahora para ejecutar el query como podria hacerle?, por que por ejemplo ise este codigo

Código Delphi [-]
type
  TBarra = class(TThread)
  private
     sesion:TOracleSession;
     script:TOracleScript;
  protected
     procedure Execute; override;
  public
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    RichEdit1: TRichEdit;
    ProgressBar1: TProgressBar;
    OracleSession1: TOracleSession;
    OracleLogon1: TOracleLogon;
    Script1: TOracleScript;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

var
cont:integer; resultado:TStrings;

{$R *.dfm}

procedure TBarra.Execute;
begin
   try
      sesion.LogonUsername:='lg';
      sesion.LogonPassword:='lg';
      sesion.LogonDatabase:='bd.world';
      sesion.Connected:=true;
      script.Session:=sesion;
      script.Lines.Add('select * from usuarios');
      script.Execute;
      resultado:=script.Output;
      sleep(2000);
      cont:=1;
   except
      on E : Exception do
      begin
         sleep(2000);
         cont:=1;
      end;
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   hilo:TBarra;
begin
   hilo:=TBarra.Create(False);
   cont:=0;
   while cont<>1 do
   begin
      ProgressBar1.StepIt;
      form1.update;
      sleep(125);
   end;
   if not(resultado=nil) then
      RichEdit1.Lines:=resultado
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   cont:=0;
end;

pero nisiquiera ejecuta el query se va directo al exception estoy haciendoalgo mal?

Chris 23-01-2009 22:55:10

No debes estar haciendo nada mal, por el contrario. Lo que debe susceder es que los componentes que estás utilizando para hacer la conexión a la DB no son "Thread Save", es por esta razón que generan una ecepción, que supongo debe ser de tipo AV. Desde este punto, lo único que puedes intentar hacer es desconectar de cualquier componente TDatasource al que esté conectado el componente de conexión.

Por otro lado, también puedes intentar co un try ... except, auque creo que con la mayoría de componentes de conexión esto no es recomendable hacerlo.

Saludos.

Chris 23-01-2009 23:07:23

Código Delphi [-]
..
 script.Session:=sesion;
 script.Lines.Add('select * from usuarios');
 script.Execute;
 resultado:=script.Output;
..
No si "script" es una variable, o es un objeto que esta ubicado en algún formulario. Si es el último caso, las anteriores líneas deben estar dentro de un procedimiento del Thread que sea llamado con synchronize:
Código Delphi [-]
TBarra.ActualizarScript;
begin
with formulario1.script do
  Session := sesion;
  lines.Add('select * from usuarios');
  script.Execute;  // esta línea podría moverse a otro método del thread (threaded), si es que no producen problemas al hacerlo.
  respaldo := script.Output;  // no quiero producir un bug al omitir "script"
end;

Lo puedes llamar así:
Código Delphi [-]
procedure TBarra.Execute;
begin
   try
      sesion.LogonUsername:='lg';
      sesion.LogonPassword:='lg';
      sesion.LogonDatabase:='bd.world';
      sesion.Connected:=true;
//      script.Session:=sesion;
//      script.Lines.Add('select * from usuarios');
//      script.Execute;
//      resultado:=script.Output;

//    Las líneas comentadas arriba fueron puestas en "ActualizarScript"
      Synchronize(ActualizarScript);

      sleep(2000);
      cont:=1;
   except
      on E : Exception do
      begin
         sleep(2000);
         cont:=1;
      end;
   end;
end;

Saludos.-

Dark_RavenM 23-01-2009 23:26:46

un objeto que cree dentro del hilo
type
TBarra = class(TThread)
private
sesion:TOracleSession;
script:TOracleScript;
protected
procedure Execute; override;
public
end;

Chris 23-01-2009 23:30:30

Ahí si que ya me la pusistes fea. :confused::confused:
Que tipo de excepción te estaba dando? y en que línea te decía. Además, por supuesto del mensaje de error.

Dark_RavenM 23-01-2009 23:45:57

Cita:

Empezado por D&W (Mensaje 335743)
No debes estar haciendo nada mal, por el contrario. Lo que debe susceder es que los componentes que estás utilizando para hacer la conexión a la DB no son "Thread Save", es por esta razón que generan una ecepción, que supongo debe ser de tipo AV. Desde este punto, lo único que puedes intentar hacer es desconectar de cualquier componente TDatasource al que esté conectado el componente de conexión.

Por otro lado, también puedes intentar co un try ... except, auque creo que con la mayoría de componentes de conexión esto no es recomendable hacerlo.

Saludos.

ya lo probe asi como me dijiste separando el codigo con una sincronisacion pero no tampoco cumple el cometido sigue entrando directo al exeption, supongo que puede ser por lo que dices que el componente no es thread save, pero cual otro componente podria utilizar para ejecutar scripts completos en un programa de delphi ya que el tquery solo es para un query simple y yo necesito ejecutar procedimientos y cosas asi que me regresen todo el resultado de la ejecucion

Dark_RavenM 23-01-2009 23:50:24

Cita:

Empezado por D&W (Mensaje 335751)
Ahí si que ya me la pusistes fea. :confused::confused:
Que tipo de excepción te estaba dando? y en que línea te decía. Además, por supuesto del mensaje de error.




lo extraño es que le puse asi


Código Delphi [-]

procedure TBarra.Execute;
begin
   try
      sesion.LogonUsername:='lg';
      sesion.LogonPassword:='lg';
      sesion.LogonDatabase:='bd.world';
      sesion.Connected:=true;
//      script.Session:=sesion;
//      script.Lines.Add('select * from usuarios');
//      script.Execute;
//      resultado:=script.Output;

//    Las líneas comentadas arriba fueron puestas en "ActualizarScript"
      Synchronize(ActualizarScript);

      sleep(2000);
      cont:=1;
   except
      on E : Exception do
      begin
         sleep(2000);
         cont:=1;
         ShowMessage(E.ClassName+' error: '+E.Message);
      end;


   end;
end;


y no me regresa el mensaje de error que esta ocurriendo solo se queda la barra de progreso ciclada

Kipow 25-02-2009 19:19:54

No se como te fue con esto, pero para ejecutar querys, procedimientos, etc dentro de un Hilo diferente al de la aplicacion deberas de crear todos los componentes en tiempo de ejecucion. Yo asi lo hago y no tengo ningun problema.

richy08 09-07-2010 23:46:44

Cita:

Empezado por Kipow (Mensaje 339322)
No se como te fue con esto, pero para ejecutar querys, procedimientos, etc dentro de un Hilo diferente al de la aplicacion deberas de crear todos los componentes en tiempo de ejecucion. Yo asi lo hago y no tengo ningun problema.

hola kipow se que es un hilo muy viejo pero es lo unico que he encontrado que se asemaje a lo que quiero hacer, sabes estoy tratando de hacer algo como lo que comentas pero estoy un poco liado podrias postear un poco de tu codigo donde creas la coneccion a la bd, y metes el codigo sql dentro de los hilos mil gracias.


La franja horaria es GMT +2. Ahora son las 00:45:35.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi