Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   abortar proceso y mostrar estado (https://www.clubdelphi.com/foros/showthread.php?t=89601)

mizzard 22-12-2015 19:18:18

abortar proceso y mostrar estado
 
Buenas tardes a todos!,

Os comento por encima lo que quiero realizar.

Estoy programando una aplicación en C++Builder XE3, la cual consulta una base de datos de Access (MDB) de clientes y con un boton mando publicidad personalizando el mail con archivos adjuntos a todos los clientes que cumplan con unos requisitos concretos (vamos una especie de SPAM automático).

El caso es que me funciona perfectamente tanto la conexión a mi BBDD, como la actualización y consulta de información y el envío de mails de forma automática. El problema que tengo es que (de momento estoy haciendo pruebas con una BBDD con pocos clientes ficticios para verificarlo) cuando le doy al botón enviar el programa se queda pensando hasta que manda todos los correos de golpe y no me actualiza las variables intermedias ni me deja la posibilidad de cancelar el envio tras terminar una transacción correcta (para evitar datos corruptos en la BBDD).

Para realizar el envio multiple hago una consulta a la BBDD con el filtrado deseado y acto seguido con un bucle for voy linea a linea enviando los mails y actualizando la BBDD hasta que termino de recorrer la tabla.

Dentro del bucle tengo el siguiente código:

Código:

                        // PROCEDEMOS A SU ENVIO
                        try{
                                IdSMTP1->Connect();
                                IdSMTP1->Send(Form1->IdMessage1);
                                contadorEmailsEnviados++;


                                DBConnectionBBDD->BeginTrans();
                                try{
                                // Actualizamos la ruta en la BBDD en la sección de configuración
                                strSQL = "UPDATE ... (no es importante)
                                cmdBBDD->CommandText = strSQL;
                                cmdBBDD->Execute();

                                DBConnectionBBDD->CommitTrans();
                                }

                                catch(Exception &exception){
                                        DBConnectionBBDD->RollbackTrans();
                                        Application->ShowException(&exception);
                                }
                        }
                        catch(Exception &exception)
                        {
                                Application->ShowException(&exception);
                                Label1->Caption = "¡Error!";
                                break;
                        }
                        IdSMTP1->Disconnect();
                        IdMessage1->Clear();

                        Label1->Caption = contadorEmailsEnviados;
        //                Application->MessageBoxW(L"Todo ok, pasamos al siguiente.",L"Info",MB_OK+ MB_ICONERROR);
                } // Fin de condicion IF (no la he pegado)
        }// fin bucle for

En Label1 muestro el nº conforme se van enviando, pero por contra el programa se queda "atascado" hasta que envia todos y entonces ya me aparece el valor final.
Sin embargo si descomento la linea donde sale el MessageBox, en el momento de envio me sale ese mensaje (que no sirve para nada), pero sí que se actualiza la variable contadorEmailsEnviados en cada envio.

¿Se os ocurre alguna forma de poder mostrar en tiempo real cada vez que se envie un email e incluso con un boton poder abortar?
¿habría que iniciar otro proceso para evitar bloquear el programa y consultar el estado de una variable para finalizar todos los pasos intermedios y asi evitar corromper la BBDD?
¿Como se haria?

Muchas gracias de antemano y un abrazo!

mizzard 22-12-2015 19:53:48

Hola de nuevo!

Parece que ya lo he resuelto, la solución se puede intuir en el siguiente hilo:

http://www.clubdelphi.com/foros/show...roceso+abortar

Al final lo he solucionado cambiando el bucle por un do...while, donde en el while se analiza un boolean (que lo cambio con el boton abortar), de esta forma e insertando Application->ProcessMessages(); parece que funciona bien.

No obstante seguiré haciendo pruebas.

Un abrazo!:)

mizzard 23-12-2015 17:26:42

Retomo el tema
 
Buenas tardes de nuevo,

Retomo el hilo porque lo estoy planteando con threads y me estoy armando un lio.

Por casualidades me he dado cuenta que cuando mando el email:

Código:

Form1->IdSMTP1->Connect();
Form1->IdSMTP1->Send(Form1->IdMessage1);

Puede darse el caso de que el servidor tarde más de lo esperado, por lo que me propongo usar threads (nunca las he usado).

Basándome en el siguiente video:
https://www.youtube.com/watch?v=1g4db90Mfgw

Adapto mi código de tal forma que parece ser que funciona, pero tengo los siguientes problemas:

1º Antes llamaba a una función que se encargaba de gestionar los envíos, pasándole un parámetro para actuar de una forma u otra (aprovechando el codigo de envio)

Ahora al tener que insertar el codigo en la Unit2.cpp dentro de la sección de void __fastcall TConnectThread::Execute() he sacado el codigo de la funcion y no sé como hacerlo para que cuando se ejecute el thread que se active la funcion que tenia antes estilo:
Código:

void enviarEmail(int opcion);
Eso o actuar sobre variables globales, pero me da a mi que es mejor pasársela por función.

2º En el Unit1.cpp

Cuando deseo llamar al Thread, utilizo:
Código:

myThread1->Start();
Ya que tengo la siguiente linea:

Código:

TConnectThread *myThread1 = new TConnectThread(true);
Al principio del todo (inmediatamente despues de los includes, de los #pragma link, TForm1 *Form1)

No sé si es que lo estoy utilizando mal o que es lo que pasa, que mi objetivo es utilizar un timer programado con un tiempo para que si se queda enganchado el servidor de envio que aborte el thread de forma drástica.

En el timer pongo:
Código:

myThread1->Terminate();
Y dentro del thread en la condicion while añado && !Terminated, pero claro funciona cuando no se engancha en el servidor
Si pruebo con esto otro
Código:

        unsigned long exitCode;
        GetExitCodeThread(myThread1, &exitCode);
        TerminateThread(myThread1, exitCode);

Parando el timer posteriormente, indicar que el thread no para, ya que continua.


CONCLUSIÓN:

Me gustaria utilizar un thread para evitar cuelgues del programa cuando se envian varios emails y el servidor puede que se caiga, para ello necesito enviar variables al thread para aprovechar la funcion de envio que utilizaba y saber como abortar en caso de que me desborde el timer

¿Alguna orientación de como hacerlo?

Muchas gracias y un abrazo!

AgustinOrtu 23-12-2015 18:31:01

No estoy seguro de que el ttimer sea thread safe.

Todo lo que sea externo al thread debe ser sincronizado para poder accederlo, consultarlo o modificarlo

Lo mejor es no usar variables globales, y los parámetros que sean copias. OJO no es un dogma, pero en muchos casos la sincronización termina siendo cuello de botella, es más práctico copiar el recurso y usarlo que compartirlo

En tu caso yo usaría un ttimer por thread, es decir, cada thread tiene su ttimer

escafandra 23-12-2015 20:29:29

Cita:

Empezado por mizzard (Mensaje 500753)
En el timer pongo:
Código:

myThread1->Terminate();
Y dentro del thread en la condicion while añado && !Terminated, pero claro funciona cuando no se engancha en el servidor
Si pruebo con esto otro
Código:

    unsigned long exitCode;
    GetExitCodeThread(myThread1, &exitCode);
    TerminateThread(myThread1, exitCode);

Parando el timer posteriormente, indicar que el thread no para, ya que continua.

El método Terminate() No asegura que el thread termine, sólo que saldrá del bucle de Execute, si lo tiene. Si el hilo está "enganchado" por el servidor, no terminará de este modo. Quizás la respuesta esté en manipular IdSMTP. Si vas a usar varios hilos, sería lo suyo si envías varios emails, éstos no deberían depender de Form1->IdSMTP1, sino tener su propio componente.

El siguiente código con la API TerminateThread acaba con el hilo fulminantemente y debe usarse con myThread1->Handle. No es la forma recomendable de terminar un hilo, pues puedes tener fugas de memoria al no liberarse correctamente.
Código:

TerminateThread(myThread1, exitCode);
Código:

TerminateThread(myThread1->Handle, exitCode);
Saludos.


La franja horaria es GMT +2. Ahora son las 16:14:53.

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