Ir al contenido


Foto

Hilos (Threads)


  • Por favor identifícate para responder
12 respuestas en este tema

#1 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 12 mayo 2010 - 11:07

Hola foro, amigos estoy intentando hacer lo siguiente:
Tengo una funcion la cual copia los ficheros de un directorio hacia otro, este proceso segun la cantidad de ficheros demora entre 30 y 40 minutos.
Esta copia se puede programar para una hoara dada (algo asi como un ntbackup), pero si hay varias programaciones para la misma hora el programa debe esperar copiar los ficheros y luego volvera  a hacerlo (para explicarme mejor: es como las impresoras que los ficheros se van almacenando en cola). Entonces vi la opcion de trabajar con hilos, y segun tengo entendido con los hilos se pueden trabajar varios procesos y funciones en paralelo. Voy bien?
Si es asi quiciera que me pudieran dar una sugerencia de como hacer las copias en paralelo y no tener que esperar por que termine una para comenzar otra (como abrir varias instancias de una funcion - si es que eso existe-).
Muchas gracias.
  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 mayo 2010 - 12:59

Ciertamente los Threads o hilos resuelven el problema del trabajo en paralelo e impiden la apariencia de una aplicación que no responde. Pero consumen recursos. Si quieres que tu copia de seguridad se haga en un hilo distinto al de tu aplicación, ésta parecerá continuar trabajando normalmente pero la copia de disco puede hacerse notar en una disminución del rendimiento general.

Cada aplicación tiene un Hilo principal y otros secundarios, así pueden trabajar varias aplicaciones al mismo tiempo. Si tu aplicación no va ha hacer ninguna otra cosa mientras trabaja, posiblemente no tengas necesidad de trasladar el código de copiado a otro thread. Puedes usar la API CopyFileEx que te permite monitorear la copia mientras sucede.

Si decides usar un Thread, no en complicado. Usa la clase TThread. Lee bien la ayuda del Builder, algún detallito es importante para que el código sea seguro. Tu código, puede avisar a tu programa principal de su terminación con un mensaje, por ejemplo.

Insisto, en tu proyecto de copia de seguridad es posible que no tengas necesidad de hilos aunque si de un monitor, véase barra de progreso, label con el fichero en curso...

Analiza bien tus necesidades, te ahorrarás trabajo.

Saludos.
  • 0

#3 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 12 mayo 2010 - 01:16

Parece que no me hice explicar bien.
Haber tengo una copia de seguridad (backup) programado para las 3pm que demorar en realizar todas las copais 30 minutos
y otro backup par las 3:15pm que demorara 40 minutos
Si utilizo una funcion y comienza a ejecutarse a las 3pm para el primer backup, cuando sean las 3:15 y le toque al segundo backup ejecutarse no podra pues la funcion estara ejecutando el primer backup, ahora no se ve muy fatal pero imaginemos un backup de mas de una hora, o por cuestiones de incapacidad (como es mi caso) . Por eso pense en hilos, esclareceme ahora si es necesario su uso o con la API que me mencionas se soluciona todo por favor. Espero que me entiendas.
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 mayo 2010 - 02:09

Pues parece que debes usar Hilos para que no se solapen las copias.

Se puede hacer con API pero Builder tiene una Clase denominada TThread (como te comenté dos mensajes arriba) que te facilitará mucho la tarea. Estudia la ayuda antes de hacer nada para familiarizarte. Básicamente se trata de definir una clase derivada de TThread. Esa clase debe tener una función miembro especial: Execute que es una sobrecarga de la función virtual del mismo nombre de la clase TThread. Esa función debe contener un bucle principal del que sólo se saldrá cuando tus condiciones se den o cuando la variable Terminated sea true (para controlar la terminación obligada del hilo). La función Execute es donde pondrás el código que ejecutará tu hilo, en este caso la copia de los archivos.

En el Builder tienes ejemplos. Mira Builder\Examples\Apps\Threads tienes un ejemplo de aplicación que usa Threads. Es un poco largo entrar en detalles. pero básicamente podrías empezar así:



cpp
  1. class TBackup : public TThread
  2. {
  3.   private:
  4.   protected:
  5.     void __fastcall Execute();
  6.  
  7.   public:
  8.     // tu consructor
  9.     __fastcall TBackup (bool CreateSuspended);
  10. };
  11.  
  12. __fastcall TBackup ::TBackup (bool CreateSuspended)  : TThread(CreateSuspended)
  13. {
  14. }
  15.  
  16. void __fastcall TBackup ::Execute()
  17. {
  18.   //---- Tu código ----
  19.   for(int n=0; tus condiciones ; n++){
  20.       // tu código repetido....
  21.       if(Terminated) break;
  22.   }
  23. }



Saludos.


  • 0

#5 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 12 mayo 2010 - 02:23

Gracias scafandra tendre que hecharle una ojeada al codigo que me sugieres de la ayuda del Builder, cualquier duda estare por aca.
Saludos
  • 0

#6 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 13 mayo 2010 - 06:48

A modo de ejemplo te muestro una forma de copiar archivos con TThread:

Archivo cabecera de la unidad del thread:


cpp
  1. //---------------------------------------------------------------------------
  2.  
  3. #ifndef Unit2H
  4. #define Unit2H
  5. //---------------------------------------------------------------------------
  6. #include <Classes.hpp>
  7. //---------------------------------------------------------------------------
  8. class ThCopyFiles : public TThread
  9. {
  10. private:
  11. protected:
  12.         void __fastcall Execute();
  13. public:
  14.         __fastcall ThCopyFiles(TStrings* Strings, String Dst);
  15.  
  16.         TStrings* Strings;
  17.         String    Destino;
  18. };
  19. //---------------------------------------------------------------------------
  20. #endif

Código del TThread:

cpp
  1. pragma hdrstop
  2. #include "Unit2.h"
  3. #pragma package(smart_init)
  4. //---------------------------------------------------------------------------
  5.  
  6. __fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, String dst)
  7.         : TThread(false), Strings(strings)
  8. {
  9.   Destino = dst;
  10.   if(Destino[Destino.Length()] != '\\') Destino = Destino + "\\";
  11.   FreeOnTerminate = true;
  12. }
  13.  
  14. //---------------------------------------------------------------------------
  15. // Crea una carpeta y las subcarpetas de una ruta dada
  16. void CreatePath(char* path)
  17. {
  18.   char Path[MAX_PATH];
  19.   char* fin = strchr(path, '\\');
  20.   while((fin = strchr(fin+1, '\\'))!=0){
  21.       *Path = 0;
  22.       strncat(Path, path, (DWORD)fin-(DWORD)path);
  23.       CreateDirectory(Path, 0);
  24.   }
  25. }
  26.  
  27. //---------------------------------------------------------------------------
  28. void __fastcall ThCopyFiles::Execute()
  29. {
  30.   String FileName;
  31.   for(int n=0; n < Strings->Count; n++){
  32.       FileName = Destino + String(Strings->Strings[n].c_str()+3);
  33.       CreatePath(ExtractFilePath(FileName).c_str());
  34.       CopyFile(Strings->Strings[n].c_str(), FileName.c_str(), false);
  35.       if(Terminated) return;
  36.   }
  37. }
  38. //---------------------------------------------------------------------------


Muestra de uso:

cpp
  1. #include "unit2.h"  // la que contiene la definición de nuestra clase Thread
  2. //...........
  3. void __fastcall TForm1::Button3Click(TObject *Sender)
  4. {
  5.   // Copiaremos la lista de archivos de Memo1 en una carpeta F:\Prueba
  6.   // creando para ello un Thread
  7.   ThCopyFiles* ThCopy = new ThCopyFiles(Memo1->Lines, "F:\\Prueba");
  8.   ThCopy->OnTerminate = CopyDone;
  9. }


Espero que el ejemplo te sirva para entender los hilos en Builder y que la función de copia te sea útil.

Saludos.
  • 0

#7 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 13 mayo 2010 - 09:07

Scafandra, muchisimas gracias por el codigoqu me dejas, en cuanto lo pruebe te comento, pero si es como dices, es tremendo.
muchas gracias por la inmensa ayuda
  • 0

#8 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 14 junio 2010 - 02:56

Hola escafandra, por razones ajenas a mi no habia podido acceder al foro.
He probado el codigo y he encontrado algunas dificultades por ejemplo

ThCopy->OnTerminate = CopyDone;

No se en que parte del codigo concuerda esta linea
muchas gracias
  • 0

#9 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 14 junio 2010 - 03:57

...He probado el codigo y he encontrado algunas dificultades por ejemplo

ThCopy->OnTerminate = CopyDone;

No se en que parte del codigo concuerda esta linea...


Esa línea está asignando la función CopyDone al evento OnTerminate del Thread. Esa función puede ser como esta:


cpp
  1. void __fastcall TForm1::CopyDone(TObject *Sender)
  2. {
  3.   ShowMessage("Terminado");
  4. }



Cuando termine el Thread se ejecutará la función CopyDone que mostrará, en este caso un mensaje... Programa tu lo que precises o nada... ;)


Saludos.
  • 0

#10 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 15 junio 2010 - 08:34

Muchas gracias escafandra.  Ya lo probe y va todo bien
  • 0

#11 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 11 agosto 2010 - 06:17

Hola scafandra, retomando el tema.... :wink:
He hecho algunos cambios en el código original para poder enviar la información a varios destinos:


cpp
  1. // ---------------------------------------------------------------------------
  2.  
  3. #include <vcl.h>
  4. #pragma hdrstop
  5.  
  6. #include "uThreadCF.h"
  7. #pragma package(smart_init)
  8.  
  9. // ---path de la base de datos
  10. extern String dirDataBase;
  11.  
  12. // ---------------------------------------------------------------------------
  13. __fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, TStrings* dst) : TThread
  14. (false), Strings(strings) {
  15.  
  16. //poniendole a cada destino el doble // si no lo tiene
  17. Destinos = dst;
  18. for (int i = 0; i < Destinos->Count; i++) {
  19.     //obteniendo cada destino por separado
  20. String dest = Destinos->Strings[i];
  21. if (dest[dest.Length()] != '\\') {
  22. dest = dest + "\\";
  23. Destinos->Strings[i] = dest;
  24. }
  25. }
  26. FreeOnTerminate = true;
  27. }
  28.  
  29. // ---------------------------------------------------------------------------
  30. // Crea una carpeta y las subcarpetas de una ruta dada
  31. void CreatePath(char* path) {
  32.  
  33. char Path[MAX_PATH];
  34. char* fin = strchr(path, '\\');
  35. while ((fin = strchr(fin + 1, '\\')) != 0) {
  36. *Path = 0;
  37. strncat(Path, path, (DWORD)fin - (DWORD)path);
  38. CreateDirectory(Path, 0);
  39. }
  40. }
  41.  
  42. // ---------------------------------------------------------------------------
  43. void __fastcall ThCopyFiles::Execute() {
  44.  
  45. String FileName;
  46. //---copia de oriegenes a un destino
  47. //recorrer source
  48. for (int s = 0; s < Strings->Count; s++) {
  49.         //recorrer destinos
  50. for (int d = 0; d < Destinos->Count; d++) {
  51. String dest = Destinos->Strings[d];
  52. FileName = dest + String(Strings->Strings[s].c_str() + 3);
  53. CreatePath(ExtractFilePath(FileName).t_str());
  54. CopyFile(Strings->Strings[s].t_str(), FileName.t_str(), false);
  55. }
  56. if (Terminated)
  57. return;
  58. }
  59. }
  60. // ---------------------------------------------------------------------------

.h

cpp
  1. #ifndef uThreadCFH
  2. #define uThreadCFH
  3. //---------------------------------------------------------------------------
  4. #include <Classes.hpp>
  5. //---------------------------------------------------------------------------
  6. class ThCopyFiles : public TThread
  7. {
  8. private:
  9. protected:
  10. void __fastcall Execute();
  11. public:
  12. __fastcall ThCopyFiles(TStrings* Strings, TStrings* dst);
  13.  
  14. TStrings* Strings;
  15. TStrings*  Destinos;
  16. };
  17. //---------------------------------------------------------------------------
  18. #endif


La duda esta en el siguiente aspecto: Yo estoy obteniendo los origenes y destinos de un .ini los almacenos en TStrings* taskSource y TStrings *taskDestiny
Un timer es el encargado de llamar al Thread para ejecutar la copia:

cpp
  1. ThCopyFiles* ThCopy;
  2. ThCopy = new ThCopyFiles(taskSource, taskDestiny);

-Si el hay varias tareas la primera, por la cantidad de ficheros a copiar se demora 2 horas, la segunda debe comenzar a los 15 min de estar ejecutándose la primera. El timer vuelve a llamar a el Thread. Hay conflicto entre la información de orígenes y destinos que almacenaban los TStrings o ya esos origenes y destinos se le pasaron al thread una vez hecha la llamada?
-Cuando llamamos al thread pones:

cpp
  1. ThCopyFiles* ThCopy;
  2. ThCopy = new ThCopyFiles(taskSource, taskDestiny);

No es necesario destruir el ThCopy con delete ?
-Por que no es necesario iniciar el ThCopy con start() y demas?
Bueno scafandra esas son algunas de mis dudas por el momento
muchas gracias y saludos 
 
  • 0

#12 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 11 agosto 2010 - 09:12

No termino de entenderte del todo pero trataré de contestarte:

-Si el hay varias tareas la primera, por la cantidad de ficheros a copiar se demora 2 horas, la segunda debe comenzar a los 15 min de estar ejecutándose la primera. El timer vuelve a llamar a el Thread. Hay conflicto entre la información de orígenes y destinos que almacenaban los TStrings o ya esos origenes y destinos se le pasaron al thread una vez hecha la llamada?

En principio no tiene porqué haber conflicto en los valores de esos Strings, pero si ocurre que ambos hilos tratan de copiar el mismo fichero a la vez, podrías tener un error.

-Cuando llamamos al thread pones:


cpp
  1. ThCopyFiles* ThCopy;
  2. ThCopy = new ThCopyFiles(taskSource, taskDestiny);


No es necesario destruir el ThCopy con delete ?

En el caso de que en el constructor no pongas a true el miembro denominado FreeOnTerminate, tendrás que destruir explicitamente el Thread. Para ello tienes que estar seguro de que ha terminado. Una buena forma puede ser en el evento OnTerminate.

-Por que no es necesario iniciar el ThCopy con start() y demas?


Tal como te lo puse arranca automáticamente, pues se construye con CreateSuspended = false:


cpp
  1. __fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, TStrings* dst) : /*esto lo hace arrancar automáticamente*/ TThread
  2. (false), Strings(strings) {
  3.         // ................................
  4. FreeOnTerminate = true;
  5. }


:wink:


Saludos.
  • 0

#13 ifrit

ifrit

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 105 mensajes
  • LocationLa Habana, Cuba

Escrito 11 agosto 2010 - 09:16

Gracias por la respuesta scafandra, ya voy entendiendo.
En otro hilo estoy posteando otra duda que tengo.
  • 0




IP.Board spam blocked by CleanTalk.