Página 1 de 1

Programa com thread só funciona na primeira vez

MensagemEnviado: 10 Dez 2006 10:46
por __JEREK__
Olá galera, estou tentando entender threads e esta dando um erro aqui não consigo
entender onde esta o problema. Fiz esse programa de teste que tem a função de
contar até 50 e mostrar o resultado na tela. Ele funciona na primeira vez que aperto
o botão, na segunda que aperto o botão não faz nada.

Código: Selecionar todos
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TMultLinha *MultLinha;
int tempo=100;

int conta=0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
        Form1 -> Label1 -> Caption = "Começou!";  // Quando apertado o botão1 mostra no label1
                                                  // a palavra começou

    conta = 0;                                 //  Zera a variável conta para que comece a contagem
                                               //  apartir do zero


        MultLinha->Resume();                      // inicia o thread
}

//---------------------------------------------------------------------------------------------------------------------------------------
//Inicializa o construtor da classe TMultlinha.
__fastcall TMultLinha::TMultLinha(bool CreateSuspended) : TThread(CreateSuspended)
{
}

//---------------------------------------------------------------------------

void __fastcall TMultLinha::Execute()
{

//conta = 0;

    while(conta<49)                            // Enquanto a variável conta for menor que 49...
    {

        conta++;                               // ...incrementa o valor de conta...

        Form1->Label2->Caption = conta;        // ... mostra o valor de conta no label2

        Sleep(tempo);                          // ... espera um tempo e se ainda conta for menor que 50
                                               // volta ao inícil do laço while

    }                                          // Fim do laço while

    Form1 -> Label1 -> Caption = "Terminou!!"; // Quando termina a contagem mostra na label1 que terminou
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{

    MultLinha = new TMultLinha(true);          // Aloca memória para o thread
    MultLinha->Priority = tpNormal;            // Define a prioridade.

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
    MultLinha->Suspend();                      // Finaliza o thread
}
//---------------------------------------------------------------------------


coloquei no arquivo .h a classe

Código: Selecionar todos
class TMultLinha : public TThread
{
    private:
    protected:
             void __fastcall Execute();
    public:
             __fastcall TMultLinha(bool CreateSuspended);
};


minha dúvida é, porque quando a contagem vai até o final (49), eu não consigo iniciar novamente se eu reseto todas as variáveis??

tem mais alguma coisa que deva fazer para poder funcionar??

outra coisa, tentei colocar um ShowMessage mas deu erro, não posso usar essa função com thread??

valeu galera!!!

MensagemEnviado: 10 Dez 2006 11:17
por Red Neck Guy
Pq o código dentro da thread é executado como se fosse uma função normal, ou seja, ela executa o teu loop while e como a condição após 49 deixa de ser verdadeira a função acaba.

Para fazer um thread rodar "para sempre" deve-se fazer assim:


for(;;){

//código que será executado na thread


}

Eu costumo fazer assim:

for(;flagThreadAtiva;){

//código da thread

}

pois daí esse flagThreadAtiva eu controlo atraves da outra thread, pois senão quando o programa acaba a parte da thread continua na memória.

MensagemEnviado: 10 Dez 2006 11:52
por __JEREK__
Oi Aquino, valeu pela resposta rápida!!!!

Quer dizer que as variáveis dentro de um thread não são zeradas??
por exemplo essa variável conta vai até 50 e eu não consigo resetar
ela??

Aquino, tentei criar uma nova classe (multilinha2) mas da erro, tentei
fazer como vc disse, criar uma nova classe de thread e colocar
ativa_threda = 0;' dentro mas qual a diferença entre zerar a variável
ativa_thred no botão de inícil e zerar atravez de outro thread (ou
sera que entndi tudo errado?? hehehe)



Código: Selecionar todos
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TMultLinha *MultLinha;
int tempo=100;

int conta=0;
int ativa_thread=0;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
        Form1 -> Label1 -> Caption = "Começou!";  // Quando apertado o botão1 mostra no label1
                                                  // a palavra começou

        conta = 0;                                //  Zera a variável conta para que comece a contagem
                                                  //  apartir do zero
        ativa_thread = 1;



        MultLinha->Resume();                      // inicia o thread
}

//---------------------------------------------------------------------------------------------------------------------------------------
//Inicializa o construtor da classe TMultlinha.
__fastcall TMultLinha::TMultLinha(bool CreateSuspended) : TThread(CreateSuspended)
{
}


//---------------------------------------------------------------------------------------------------------------------------------------
//Inicializa o construtor da classe TMultlinha.
__fastcall TMultLinha2::TMultLinha2(bool CreateSuspended) : TThread(CreateSuspended)
{
}

//---------------------------------------------------------------------------

void __fastcall TMultLinha2::Execute()
{

ativa_thread =0;

}


//---------------------------------------------------------------------------

void __fastcall TMultLinha::Execute()
{

//conta = 0;

   // while(conta<49)                            // Enquanto a variável conta for menor que 49...
   for(;ativa_thread;)
    {

        conta++;                               // ...incrementa o valor de conta...

        Form1->Label2->Caption = conta;        // ... mostra o valor de conta no label2

        Sleep(tempo);                          // ... espera um tempo e se ainda conta for menor que 50
                                               // volta ao inícil do laço while
        if(conta == 50)
        {
                ativa_thread =0;
                MultLinha2->Resume();                      // inicia o thread    <-------------------Erro 'Undefinide symbol'
        }


      //   MultLinha->Resume();                      // inicia o thread

    }                                          // Fim do laço while

    ativa_thread = 0;
    Form1 -> Label1 -> Caption = "Terminou!!"; // Quando termina a contagem mostra na label1 que terminou
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{

    MultLinha = new TMultLinha(true);          // Aloca memória para o objeto.
    MultLinha->Priority = tpNormal;            // Define a prioridade.

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{


    MultLinha->Suspend();                      // Finaliza o thread
}
//---------------------------------------------------------------------------



Aquino. mais uma vez obrigado pela resposta!!!

MensagemEnviado: 10 Dez 2006 13:04
por Red Neck Guy
Não é nada a ver com o fato de não zerar uma variavel, faça um paralelo com a função main, se dentro dela não houver nenhum laço de repetição o teu programa será executado apenas uma vez e terminará, é isso que ocorre...

MensagemEnviado: 10 Dez 2006 14:00
por __JEREK__
Ah!!!!!!!!!!!!! entendi!! poxa, se era só isso por que eles não falam logo o que esse tal de thread???

Valeu Aquino, agora tá funcionando legal!!!

vou deixar o código aqui se alguem tiver a mesma dúvida depois dessa explicação sua acho que vai pegar rapidinho o conseito de thread!!!


Aqui é o Form1, eu só coloquei o objeto Tabsheet para ter certeza que o thread esta funcionando mesmo e não travando o formulario.

Imagem

Código do Unit.ccp
Código: Selecionar todos
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

TMultLinha *MultLinha;                                // cria Mutilinha , como se fosse Form1
int tempo=100;                                        // tempo de Sleep
int conta=0;                                          // variável conta

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}

//---------------------------------------------------------------------------
//Inicializa o construtor da classe TMultlinha.
__fastcall TMultLinha::TMultLinha(bool CreateSuspended) : TThread(CreateSuspended)
{
}

//---------------------------------------------------------------------------

void __fastcall TMultLinha::Execute()
{


  while(true)                                         // Vida do thread
    {
        conta++;                                      // ...incrementa o valor de conta...

        if(conta<50)                                  // ... se o valor de conta for menor que 50...
        {

           Form1->Label2->Caption = conta;            // ... mostra o valor de conta no label2...

        }

        if(conta==50)                                 // ... se o valor de conta for igual a 50 ...
        {

           Form1 -> Label1 -> Caption = "Terminou!!"; // ... mostra na label1 que terminou

        }

        Sleep(tempo);                                 // ... quando termina o processo o programa entra
                                                      // em Sleep, ou seja não fica processando nada e
                                                      // tomando memória mas tambem não termina

    }                                                 // Fim do laço while

}
//---------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
        Form1 -> Label1 -> Caption = "Começou!";      // Quando apertado o botão1 mostra no label1
                                                      // a palavra começou

        conta = 0;                                    // Zera a variável conta para que comece a contagem
                                                      // apartir do zero

        MultLinha->Resume();                          // inicia o thread
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    MultLinha->Suspend();                             // Finaliza o thread
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    MultLinha = new TMultLinha(true);                 // Aloca memória para o thread
    MultLinha->Priority = tpNormal;                   // Define a prioridade.
}
//---------------------------------------------------------------------------


Código do Unit.h
Código: Selecionar todos
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
//---------------------------------------------------------------------------
class TMultLinha : public TThread             // Acresentar classe do thread
{
    private:
    protected:
             void __fastcall Execute();
    public:
             __fastcall TMultLinha(bool CreateSuspended);
};
//----------------------------------
class TForm1 : public TForm
{
__published:   // IDE-managed Components
        TButton *Button1;
        TButton *Button2;
        TLabel *Label1;
        TLabel *Label2;
        TPageControl *PageControl1;
        TTabSheet *TabSheet1;
        TTabSheet *TabSheet2;
        TTabSheet *TabSheet3;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button2Click(TObject *Sender);
        void __fastcall FormCreate(TObject *Sender);
private:   // User declarations
public:      // User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif


Aquino, muito obrigado pela exlicação cara, valeu mesmo!!

abraços e boa sorte!!!

MensagemEnviado: 10 Dez 2006 14:11
por Red Neck Guy
Só um pequeno detalhe.
Como tu colocaste um while(true) dentro do da thread quando o teu programa acabar a área de memória alocada pela tua aplicação no windows continuará reservada, o correto é implementar o flag como eu havia mencionado e no método destrutor da classe pai zerá-lo, assim quando o programa acabar a thread morre.

MensagemEnviado: 10 Dez 2006 15:48
por CCandido
achei que é melhor maneira de usar Thread

cpp code
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------
HANDLE hThread; DWORD dwID;
bool Tterminar=true;
DWORD WINAPI ThreadFunc(LPVOID lParam);
//---------------------------------------------------------

//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
hThread = CreateThread(NULL,0,ThreadFunc,NULL,0,&dwID);

}
//--------------------------------------------------------------------------
//---------------------------------------------------------------------------
DWORD WINAPI ThreadFunc(LPVOID lParam){

while( Tterminar ){

aqui seus codigos..............

}


}

//----------------------
aqui FormClose ou Sair click pra finalizar

Tterminar=false;

MensagemEnviado: 10 Dez 2006 16:37
por __JEREK__
CCandido, muito obrigado pela sua contribuição, já me deu outras idéias.

eu gostaria de saber uma coisa se for possível, como eu sei se o thread realmente foi fechado e a area de memória que foi reservada esta livre, existe algum jeito??

Valeu galera, muito obrigado!!!

MensagemEnviado: 10 Dez 2006 23:52
por CCandido
cara depois disto aqui ( Tterminar=false; ) ela finaliza mesmo!
só que tem que ser na saida do programa
ex: no Sair_click e no FormClose

não testei ainda mas axo que pode finalizar uma T, e abrir outra e vice verça.
em caso de seu pg. poder entrar num loopforerer use truques
ex: uma tecla que finalize a T. e depois o programa.
esta parte que eu mais gosto (waxi_dog_forçado).
qquer coisa use o msn ccandido_@hotmail.com
tLA,

MensagemEnviado: 11 Dez 2006 16:13
por chipselect
Como você está usando o CBuilder, dê uma olhada também na função Synchronize() para que o código da thread atualize de forma segura os componentes da VCL e etc...

Quando suas threads (o main roda numa) ficarem complexas e forem trocar dados, veja também o uso de semáforos ou seções críticas. Você pode usar a TThreadList no lugar de uma TList, que tem muita coisa pronta.