
Hilos (Threads)
#1
Posted 12 May 2010 - 11:07 AM
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.
#2
Posted 12 May 2010 - 12:59 PM
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.
#3
Posted 12 May 2010 - 01:16 PM
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.
#4
Posted 12 May 2010 - 02:09 PM
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í:
class TBackup : public TThread { private: protected: void __fastcall Execute(); public: // tu consructor __fastcall TBackup (bool CreateSuspended); }; __fastcall TBackup ::TBackup (bool CreateSuspended) : TThread(CreateSuspended) { } void __fastcall TBackup ::Execute() { //---- Tu código ---- for(int n=0; tus condiciones ; n++){ // tu código repetido.... if(Terminated) break; } }
Saludos.
#5
Posted 12 May 2010 - 02:23 PM
Saludos
#6
Posted 13 May 2010 - 06:48 AM
A modo de ejemplo te muestro una forma de copiar archivos con TThread:
Archivo cabecera de la unidad del thread:
//--------------------------------------------------------------------------- #ifndef Unit2H #define Unit2H //--------------------------------------------------------------------------- #include <Classes.hpp> //--------------------------------------------------------------------------- class ThCopyFiles : public TThread { private: protected: void __fastcall Execute(); public: __fastcall ThCopyFiles(TStrings* Strings, String Dst); TStrings* Strings; String Destino; }; //--------------------------------------------------------------------------- #endif
Código del TThread:
pragma hdrstop #include "Unit2.h" #pragma package(smart_init) //--------------------------------------------------------------------------- __fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, String dst) : TThread(false), Strings(strings) { Destino = dst; if(Destino[Destino.Length()] != '\\') Destino = Destino + "\\"; FreeOnTerminate = true; } //--------------------------------------------------------------------------- // Crea una carpeta y las subcarpetas de una ruta dada void CreatePath(char* path) { char Path[MAX_PATH]; char* fin = strchr(path, '\\'); while((fin = strchr(fin+1, '\\'))!=0){ *Path = 0; strncat(Path, path, (DWORD)fin-(DWORD)path); CreateDirectory(Path, 0); } } //--------------------------------------------------------------------------- void __fastcall ThCopyFiles::Execute() { String FileName; for(int n=0; n < Strings->Count; n++){ FileName = Destino + String(Strings->Strings[n].c_str()+3); CreatePath(ExtractFilePath(FileName).c_str()); CopyFile(Strings->Strings[n].c_str(), FileName.c_str(), false); if(Terminated) return; } } //---------------------------------------------------------------------------
Muestra de uso:
#include "unit2.h" // la que contiene la definición de nuestra clase Thread //........... void __fastcall TForm1::Button3Click(TObject *Sender) { // Copiaremos la lista de archivos de Memo1 en una carpeta F:\Prueba // creando para ello un Thread ThCopyFiles* ThCopy = new ThCopyFiles(Memo1->Lines, "F:\\Prueba"); ThCopy->OnTerminate = CopyDone; }
Espero que el ejemplo te sirva para entender los hilos en Builder y que la función de copia te sea útil.
Saludos.
#7
Posted 13 May 2010 - 09:07 AM
muchas gracias por la inmensa ayuda
#8
Posted 14 June 2010 - 02:56 PM
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
#9
Posted 14 June 2010 - 03:57 PM
...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:
void __fastcall TForm1::CopyDone(TObject *Sender) { ShowMessage("Terminado"); }
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.
#10
Posted 15 June 2010 - 08:34 AM
#11
Posted 11 August 2010 - 06:17 AM
Hola scafandra, retomando el tema....
He hecho algunos cambios en el código original para poder enviar la información a varios destinos:
// --------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "uThreadCF.h" #pragma package(smart_init) // ---path de la base de datos extern String dirDataBase; // --------------------------------------------------------------------------- __fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, TStrings* dst) : TThread (false), Strings(strings) { //poniendole a cada destino el doble // si no lo tiene Destinos = dst; for (int i = 0; i < Destinos->Count; i++) { //obteniendo cada destino por separado String dest = Destinos->Strings[i]; if (dest[dest.Length()] != '\\') { dest = dest + "\\"; Destinos->Strings[i] = dest; } } FreeOnTerminate = true; } // --------------------------------------------------------------------------- // Crea una carpeta y las subcarpetas de una ruta dada void CreatePath(char* path) { char Path[MAX_PATH]; char* fin = strchr(path, '\\'); while ((fin = strchr(fin + 1, '\\')) != 0) { *Path = 0; strncat(Path, path, (DWORD)fin - (DWORD)path); CreateDirectory(Path, 0); } } // --------------------------------------------------------------------------- void __fastcall ThCopyFiles::Execute() { String FileName; //---copia de oriegenes a un destino //recorrer source for (int s = 0; s < Strings->Count; s++) { //recorrer destinos for (int d = 0; d < Destinos->Count; d++) { String dest = Destinos->Strings[d]; FileName = dest + String(Strings->Strings[s].c_str() + 3); CreatePath(ExtractFilePath(FileName).t_str()); CopyFile(Strings->Strings[s].t_str(), FileName.t_str(), false); } if (Terminated) return; } } // ---------------------------------------------------------------------------
.h
#ifndef uThreadCFH #define uThreadCFH //--------------------------------------------------------------------------- #include <Classes.hpp> //--------------------------------------------------------------------------- class ThCopyFiles : public TThread { private: protected: void __fastcall Execute(); public: __fastcall ThCopyFiles(TStrings* Strings, TStrings* dst); TStrings* Strings; TStrings* Destinos; }; //--------------------------------------------------------------------------- #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:
ThCopyFiles* ThCopy; 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:
ThCopyFiles* ThCopy; 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
#12
Posted 11 August 2010 - 09:12 AM
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.-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 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.-Cuando llamamos al thread pones:
cpp
ThCopyFiles* ThCopy; 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?
Tal como te lo puse arranca automáticamente, pues se construye con CreateSuspended = false:
__fastcall ThCopyFiles::ThCopyFiles(TStrings* strings, TStrings* dst) : /*esto lo hace arrancar automáticamente*/ TThread (false), Strings(strings) { // ................................ FreeOnTerminate = true; }

Saludos.
#13
Posted 11 August 2010 - 09:16 AM
En otro hilo estoy posteando otra duda que tengo.