Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 16-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Error en hilos de ejecución

Hola a todos!

Este tema se que se ha tocado varias veces, y por días he estado revisando todo lo que he encontrado. No es primera vez que he trabajado con hilos y me ha funcionado perfectamente. pero en esta ocasión ha sido diferente. Les comento:

Necesito ejecutar dos hilos en una aplicación para rescatar datos de dos tablas diferentes. para ello estoy usando los componentes Firedac, la conexión es ODBC a un proveedor externo.

Los hilos deben ejecutarse en un orden, osea primero uno hilo, después el otro, por lo cual lo que hago es que cuando termina el primer hilo lanzo el segundo.

acá la clase que crea los hilos

Código Delphi [-]
 THiloCheque = Class(TThread)
    Constructor Create(cone: TFDConnection; const SQL: String);
    private
       FSQL: String;
       FConnection: TFDConnection;
       FConsulta: TFDQuery;
    Protected
       Procedure Execute; override;
    Public
       property Consulta: TFDQuery Read FConsulta Write FConsulta;
  End;

  //cheques detalles
  THiloChequeDet = Class(TThread)
    Constructor Create(cone: TFDConnection; const SQL: String);
    private
       FSQL: String;
       FConnection: TFDConnection;
       FConsulta: TFDQuery;
    Protected
       Procedure Execute; override;
    Public
       property Consulta: TFDQuery Read FConsulta Write FConsulta;
  End;

y por poner el ejemplo del primer hilo ya que ambos son idénticos, les dejo el código de creación y ejecución:

Código Delphi [-]
{ THiloCheque }
constructor THiloCheque.create (cone: TFDConnection; const SQL: String);
begin
  inherited Create(false);
  FConnection := cone;
  FSQL := SQL;
end;

procedure THiloCheque.Execute;
begin
  inherited;
  CoInitialize(nil);
  consulta := TFDQuery.Create(application);
  With consulta do begin
      try
        ResourceOptions.EscapeExpand:=false;
        ResourceOptions.MacroCreate :=false;
        ResourceOptions.MacroExpand :=false;
        Connection := FConnection;
        SQL.Add(FSQL);
        Open;
      Except
         on e:Exception do
             errores('Error en la captura de cheques: ' + e.Message );
     end;
  end;
  CoUnInitialize;
end;


Estos hilos se ejecutan cada 20 segundos aproximadamente y actualizan una grilla de datos, el código de la llamada sería este:

primero el hilo que me trae los datos del detalle de los cheques

Código Delphi [-]
 With THiloChequeDet.Create(v_con, 'Select * from lineasCheck ') do
        begin
          FreeOnTerminate := true;
          OnTerminate := TerminaHiloChequeDet;
        end;

cuando termina este primer hilo se realizan dos operaciones:
1- Se asigna el valor de la consulta a un componente FDMmemTable, previamente creado.
2- Se lanza el segundo hilo

acá el código

Código Delphi [-]
procedure TFPrincipal.TerminaHiloChequeDet(Sender: TObject);
begin
  
  if consCheqDet.Active then consCheqDet.close;

  if THiloChequeDet(Sender).Consulta.Active  then
   consCheqDet.Data := THiloChequeDet(Sender).Consulta.Data;  

  THiloChequeDet(Sender).Consulta.Free;

 
  With THilocheque.Create(v_con,  'Select * from check' ) do
      begin
        FreeOnTerminate := true;
        OnTerminate := TerminaHiloCheque;
      end;
end;

Cuando termina el segundo hilo básicamente se hace lo mismo, se actualizan los datos y se libera el objeto consulta que es el TFDQuery creado en el hilo<

Problema:

Lo que sucede es que esto se ejecuta cada x segundos programados en un TTimer, funciona bien pero al cabo de unas horas o el programa se queda congelado, o las consultas nunca terminan, o salta una excepción con memoria o recursos insuficientes. Entonces me pregunto porqué puede estar pasando esto si los hilos en buena teoría se deberían estar destruyendo al terminar el proceso y además el único objeto que se crea en el hilo como tal es "Consulta!, y lo destruyo cuando este termina de ejecutarse. por otro lado el segundo hilo se ejecuta cuando termina el primero.

Alguna idea?

Gracias
Responder Con Cita
  #2  
Antiguo 16-03-2019
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
¿Cuántos registros aproximadamente devuelve la sentencia 'select * from lineascheck' y cuánto tarda en ejecutarse?
Responder Con Cita
  #3  
Antiguo 16-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Son pocos los registros que trae, en las pruebas que estoy haciendo no más de 5, y demora unos 3, 4 segundos
Responder Con Cita
  #4  
Antiguo 16-03-2019
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.021
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
¿4 registros y 4 segundos?

¿Para qué se necesitan los hilos?
Responder Con Cita
  #5  
Antiguo 16-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Los hilos se necesitan porque la consulta puede ser mayor, además el tema es que esta aplicación va a conectarse a una base de datos de una empresa que está trabajando todo el tiempo con su plataforma y los registros que genere de cheques van a ser procesados por esta app externa. la idea es que trabaje en segundo plano sin intervención del usuario.
Responder Con Cita
  #6  
Antiguo 18-03-2019
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.233
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
¿Seguro que estás liberando todos los recursos creados?
Prueba a activar ReportMemoryLeaksOnShutdown (busca en los foros si no te suena) para ver si se te están quedando recursos "colgados".
¿Dónde están definidos los procedimientos: TerminaHiloChequeDet y TerminaHiloCheque?
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #7  
Antiguo 18-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Buenos días.

Gracias por responder, les comento que sigo complicado con este asunto que parece tan sencillo.

He decidido crear una unidad aparte para probar la aplicación sin hilos y me doy cuenta que el uso de la memoria aumenta considerablemente hasta que se llega al out memory. acá dejo el código, solo creo un objeto TADOconnection y un TADODataset que se destruyen a ki entender correctamente.

Ya he activado el ReportMemoryLeaksOnShutDown := True y no me arroja ninguna violación de memoria.

Código Delphi [-]
procedure importaDatos(tipoTransaccion:Integer);
var
  tabla: TAdoDataset;
  cone: TAdoConnection;
begin
   cone := TAdoconnection.Create(application);
   cone.ConnectionString := cadenaConexion;
   cone.LoginPrompt := false;
   try
     cone.Connected := true;
     tabla := TAdoDataset.Create(application);
     tabla.Connection := cone;

     if (tipoTransaccion = 0) or (tipoTransaccion = 1) then begin
        conecta.cheq.Close;conecta.cheqDet.Close;
        try
          tabla.close;
          tabla.CommandText := 'Select * from checkLine ';
          tabla.Open;
          conecta.cheqdet.Recordset := tabla.Recordset;
        Except
          on e:Exception do
             InsertaLog ('Error al capturar los detalles de los cheques ' + e.message);
        end;
     end;
   Except
     on e:exception do begin
        InsertaLog('No se pudo establecer la conexion ' + e.Message);
        cone.Free;
        tabla.Free;
        exit;
     end;
   end;
   tabla.close;
   cone.close;
   tabla.free;
   cone.free; 
end;


Este código lo ejecuto cada unos 20 segundos en un Timer
Responder Con Cita
  #8  
Antiguo 18-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Bueno, sigo probando y con solo ejecutar el código de conexión a la Base de Datos comienza a subir el consumo de memoria. En buena teoría se ejecute o no la conexión al final debería destruirse el objeto.

Código Delphi [-]
try
      cone.Connected := true;
 Except

 end;
 cone.Free;
Responder Con Cita
  #9  
Antiguo 18-03-2019
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.233
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por cmfab Ver Mensaje
Ya he activado el ReportMemoryLeaksOnShutDown := True y no me arroja ninguna violación de memoria.

Eso no te va a reportar ninguna violación de memoria.
Sirve para (como bien dice su nombre) reportar pérdidas de memoria (normalmente por recursos no liberados correctamente) al finalizar la aplicación.
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #10  
Antiguo 18-03-2019
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.233
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por cmfab Ver Mensaje
acá dejo el código, solo creo un objeto TADOconnection y un TADODataset que se destruyen a ki entender correctamente.

Personalmente creo que deberías utilizar try..finally para liberar recursos, en lugar de hacerlo en los try..except
Cada estructura tiene su función y el try..except se usa para capturar excepciones.
Mezclar la gestión de errores, con la liberación de recursos, no me parece buena idea.

AÑADO: Además veo que en algún caso, puedes estar intentando destruir cosas que aun no has creado (por ejemplo, si se produce algún problema al conectar).
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #11  
Antiguo 18-03-2019
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.911
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
Como mides el consumo de memoria? Usar el task manager, no importa el OS, no es muy util porque los OS actuales cachean memoria que da loco y pueden reportar que "usan" el 90% de la memoria pero no es la que EN ESTE INSTANTE esta usando la app, es la que el OS le cachea en el tiempo.

Tienes que usar un profiler especializado, o mirar los contadores de windows para precision....

-----

Hay varias cosas que resaltan, pero estas son las mas sospechosas:

1- Lo PEOR: Usas referencias globales! MAXIMO ERROR EN MULTI HILOS! NUNCA usar globales en multi hilos. Nunca. Eso no solo crea contención, sino que GARANTIZA DEADLOOCKS en un lenguaje como Delphi.

2- Me late que el mayor problema es:

Código Delphi [-]
conecta.cheqdet.Recordset := tabla.Recordset;

Como le asignas un POINTER de un objeto a OTRO, usando una INTERFACE que es refcounted????

3- Usar un timer... ok, pero es mucho mas simple si usas un ciclo con un sleep. Luego mira como reestructuras el código para formar una maquina de estados. Eso debe hacer el código mas claro y eliminar bugs que da miedo. Esto ademas elimina el problema de que cuando tengas un error, vas a reintentar de forma infinita hasta que muera el programa, que asi es como esta...

4- O mejor usa una librería adecuada para eso, quizás http://docwiki.embarcadero.com/RADSt...amming_Library.
__________________
El malabarista.
Responder Con Cita
  #12  
Antiguo 18-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Muchas Gracias por la respuesta de todos, aún no me queda claro el tema, voy a revisar las propuestas y les comento, por ahora decirles que conecta.cheq es un TADOdataset que se representa en una grilla de datos, lo hago de esta forma pasando el Recordset del objeto tabla para poder abrir y cerrar automáticamente una conexión a una base de datos externa. No se si tienen otra idea, pensé en llenar los datos que devuelve el TADODataset Tabla en un TClientDataset, pero como no se la estructura de los campos(nombres y tipos) me complica un poco.
Responder Con Cita
  #13  
Antiguo 19-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Buenos días a todos.

usando la instrucción
Código Delphi [-]
ReportMemoryLeaksOnShutDown := True;

obtengo el mensaje que adjunto (no supe como incrustarlo acá en el mensaje). Me pueden indicar como descifrarlo, porque no lo entiendo muy bien.

Gracias de antemano!
Imágenes Adjuntas
Tipo de Archivo: png imagen.png (7,2 KB, 10 visitas)
Responder Con Cita
  #14  
Antiguo 19-03-2019
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.233
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Fíjate que los tres primeros hacen referencia a clases; TParameters, TList y Tbquix_hilo
Te está diciendo que al acabar el programa no has liberado elemento/s de esas clases.

Si escribes un programa con este código:

Código Delphi [-]
var
  lista1:TList;
begin
  lista1 := TList.Create;
  lista1.Add(Sender);
  // lista1.Free;

Lo ejecutas y lo cierras, obtendrás este mensaje:



Si descomentas la línea del Free y lo vuelves a ejecutrar, verás desaparece.

Por lo tanto en el que te da a tí, revisa los lugares donde estás creando elementos de esas clases para ver si los estás destruyendo correctamente. A medida que vayas corrigiendo los errores, deberían desaparecer de ese mensaje.
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #15  
Antiguo 19-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
OK. Muchas Gracias por la aclaración, voy a revisarlo de inmediato, comento los resultados
Responder Con Cita
  #16  
Antiguo 19-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Bueno, sigo sin poder resolver el problema. El consumo excesivo de la memoria está en el hilo, ya lo he reprogramado de muchas maneras, he usado un Timer sin el hilo y todo sigue mal. no veo que se queden sin liberar recursos, en el resultado de la instrucción
Código Delphi [-]
ReportMemoryLeaksOnShutDown := True;
lo que tengo en esta clase son 6 strings con contenidos SQL, no se porque me hace referencia a ellos. acá el código de la clase del hilo

Código Delphi [-]
Tbquix_hilo = Class(TThread)
    Constructor Create(ConnectionString,SQL1,SQL2,SQL3,SQL4,SQL5,SQL6:String; tipoT:Integer);
  private
    FSQL1,FSQL2,FSQL3,FSQL4,FSQL5,FSQL6: String;
    FConnection: String;
    FTipoT:Integer;
    Data1,Data2, Data3, Data4,Data5, Data6: TADODataset;
  Protected
    Procedure Execute; override;
  public
    Property DataCheqDet: TADODataset read Data1 write Data1;
    Property DataCheq: TADODataset read Data2 write Data2;

    Property DataFactDet: TADODataset read Data3 write Data3;
    Property DataFact: TADODataset read Data4 write Data4;

    Property DataCMDet: TADODataset read Data5 write Data5;
    Property DataCM: TADODataset read Data6 write Data6;
  End;


la definición del constructor

Código Delphi [-]
constructor Tbquix_hilo.Create(ConnectionString,SQL1,SQL2,SQL3,SQL4,SQL5,SQL6:String; tipoT:Integer);
begin
  inherited Create(false);
  FConnection := Connectionstring;
  FSQL1 := SQL1;
  FSQL2 := SQL2;
  FSQL3 := SQL3;
  FSQL4 := SQL4;
  FSQL5 := SQL5;
  FSQL6 := SQL6;
  FTipoT := tipoT;
end;

y el método de ejecución

Código Delphi [-]
procedure Tbquix_hilo.Execute;
begin
  inherited;
  CoInitialize(nil);
  if (FtipoT = 0) or (FtipoT = 1) then begin
        Data1 := TAdodataset.Create(Application);
        Data1.ConnectionString := FConnection;
        Data1.commandtext := FSQL1;
        Data1.Open;
  end;
  CoUnInitialize;
end;

y cuando el hilo termina lo único que hago es leer los datos de ADODataset y copiarlos a un TClientDataset, después libero los objetos con esta instrucción:

Código Delphi [-]
Tbquix_hilo(Sender).DatacheqDet.Free;

y así para las 6 consultas, parece muy básico y sencillo.

no tengo idea porque se dispara la memoria, no actualizo controles de la VCL en el hilo, no uso variables globales. Que puede estar pasando ?.
Responder Con Cita
  #17  
Antiguo 20-03-2019
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.233
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Falta código y todavía quedan dudas...

¿Los elementos que creas en el hilo, dónde los destruyes? (por norma lo que se crea en un sitio se destruye en ese mismo sitio

¿Utilizas Shyncronize?
¿Porqué no usas elDestroy del hilo?
No me queda claro que hagas el CoUnInitialize y luego continues trabajando con el Dataset (yo lo pasaría al Destroy).
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #18  
Antiguo 20-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Buenos días.

Cuando el hilo se termina el Dataset creado en el mismo se destruye con la instrucción que puse anteriormente.

Los hilos se crean con la instrucción
Código Delphi [-]
FreeOnTerminate := True
, por lo cual deduzco que la propia clase hace el trabajo.

No uso el método Synchronize, puesto que lo único que hago es cuando el hilo termina y me devuelve los datos de la consulta es insertarlo en un TClientDataSet
Responder Con Cita
  #19  
Antiguo 20-03-2019
hal1967 hal1967 is offline
Miembro
 
Registrado: feb 2012
Posts: 32
Poder: 0
hal1967 Va por buen camino
El uso de "TFDConnection"

Siendo que pasasen el constructor "TFDConnection"

realmente no se que más hay con esa conexión (otros dataset o grillas). Asegurate que solo se use en ese hilo,
o no la pases al constructor, solo creala dentro del hilo.


Código Delphi [-]
constructor THiloCheque.create (cone: TFDConnection; const SQL: String);


Puedes hacerlo como prueba, ignora el pase de parámetros y creala en el constructor, y la destruyes en el destroy.
Responder Con Cita
  #20  
Antiguo 20-03-2019
cmfab cmfab is offline
Miembro
 
Registrado: jun 2010
Posts: 419
Poder: 14
cmfab Va por buen camino
Ok, Gracias, revisaré como se propone. aunque hay acá al parecer otro tema después de tantos quebraderos de cabeza, y es que algo no anda bien con el propio ODBC del proveedor, cuando hago una sola conexión la app funciona mejor, pero cuando hago conexiones dinámicas y las destruyo, ahí consume más recursos. Estoy haciendo pruebas al respecto.
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Ejecución bloqueada. Hilos de ejecución muli Desarrollo en Delphi para Android 10 28-11-2016 16:16:03
Crear Hilos de Ejecución con TIdFTP JAI_ME Varios 12 13-12-2013 19:28:39
Ayuda con Hilos de ejecucion kurono Varios 19 15-01-2011 16:36:40
Problema con hilos de ejecucion gueritox OOP 1 14-08-2010 16:26:06
Hilos de ejecucion el toluca Varios 2 29-06-2004 23:59:04


La franja horaria es GMT +2. Ahora son las 19:44:13.


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
Copyright 1996-2007 Club Delphi