Kilka pytań o wielowątkowość

0

Piszę program w C++, mam parę pytań:

Mam obiekt reprezentujący wątek, czy muszę na końcu metody Execute wywoływać metody Free oraz Terminate jeżeli od razu po zakończeniu Execute obiekt ten będzie usunięty z pamięci operatorem delete?

Za każdym razem gdy chce usunąć obiekt typu TThread, który ma ustawiony FreeOnTerminate na true program się wysypuje, czy wie ktoś dlaczego tak się dzieje?

Mam pewien obiekt typu Tthread, zaplanowałem sobie, że za każdym razem gdy użytkownik naciśnie button`a będzie wykonywana metoda Execute (oczywiście za pomocą Resume), niestety wątek zostaje odpalony tylko za pierwszym razem, potem już metoda Resume nie uruchamia Execute. Da się zrobić taki obiekt-wątek wielokrotnego użytku czy może trzeba za każdym razem tworzyć nowy obiekt??

0
kolpo napisał(a)

Mam obiekt reprezentujący wątek, czy muszę na końcu metody Execute wywoływać metody Free oraz Terminate jeżeli od razu po zakończeniu Execute obiekt ten będzie usunięty z pamięci operatorem delete?
po 1 nie da się zrobić free na samym sobie, po 2 Terminate jest zupełnie do czego innego.

Za każdym razem gdy chce usunąć obiekt typu TThread, który ma ustawiony FreeOnTerminate na true program się wysypuje, czy wie ktoś dlaczego tak się dzieje?
i pewnie nie przyszło ci do głowy sprawdzić w helpie|googlach co robi FreeOnTerminate, BTW bardzo ciężko się domyśleć po nazwie...

Mam pewien obiekt typu Tthread, zaplanowałem sobie, że za każdym razem gdy użytkownik naciśnie button`a będzie wykonywana metoda Execute (oczywiście za pomocą Resume), niestety wątek zostaje odpalony tylko za pierwszym razem, potem już metoda Resume nie uruchamia Execute. Da się zrobić taki obiekt-wątek wielokrotnego użytku czy może trzeba za każdym razem tworzyć nowy obiekt??
może poczytaj cokolwiek o wątkach najpierw bo z tego co piszesz to pojęcia nie masz jak one działają.

Podstawowa wiedza ci nie zaszkodzi, a wysiłek włożony w jej zdobycie nie powinien cię zabić ...

0

Miałem ten sam problem, ale okazało się, że uzupełnienie początku destruktora o linijki:

while(suspended) resume();
WaitFor();

rozwiązuje problem (mimo, że to trochę dziwne).
Linijki te są potrzebne tylko wtedy, gdy w destruktorze zwalniasz pewne zasoby.
Innym rozwiązaniem jest rezygnacja z destruktora i zwalnianie zasobów na końcu Execute().

0

Marek... czekaj, jaki ten sam problem? Że niby to niżej?

chce usunąć obiekt typu TThread, który ma ustawiony FreeOnTerminate na true program się wysypuje, czy wie ktoś dlaczego tak się dzieje

no nie róbcie sobie jaj, proszę ja was bardzo. Wątek się zwalnia automatycznie po wykonaniu, wy próbujecie zwolnić zwolniony już obiekt, czyli NIEISTNIEJĄCY, i robicie wielkie oczy, że program się sypie?

a Suspend / Suspended/ Resume służą do wstrzymywania i kontynuowania wątku. Takie Pause/Play. Co to w ogóle robi w destruktorze?

Znaczy ok, rozwiązanie zmyślne, tak na mój gust, po nie pozwala się zniszczyć obiektowi, który został wstrzymany, tylko wymusza pełne wykonanie zawartości Run, zanim wątek wyląduje w krainie wiecznego wywłaszczania. To mogłoby mieć zastosowanie w wątku, który musi po sobie zostawić spójne dane, na których pracuje. Może w VCL/WinAPI można to zrobić inaczej, ale to mi się podoba, nawet mimo tego, że może brzydko zakleszczyć program ;P

No ale nie ma to związku z podwójnym deletem, albo wywoływaniem resume x razy pod rząd na działającym już wątku:

za każdym razem gdy użytkownik naciśnie button`a będzie wykonywana metoda Execute (oczywiście za pomocą Resume),

Bo to są błędy, do jasnej Anielki!

0

Da się zrobić taki obiekt-wątek wielokrotnego użytku czy może trzeba za każdym razem tworzyć nowy obiekt??

Da się , przy pomocy tworzenia nowego obiektu "wątku" i zakończeniu poprzedniego .

0

@Ranides: założyłem, że niszczy obiekt jedynie przez ustawienie FreeOnTerminate=true; a nie potem jeszcze przez zastosowanie delete.
A ten problem u mnie pojawił się tutaj.

0

Aaaa, jak takie założenie zrobiłeś, to zwracam honor. Pętla tak jak mówisz, dziwna. W teorii powinna (IMO), jak pisałem, po prostu podczas destrukcji odpauzowywać wstrzymany wątek i czekać na jego zakończenie.

W teorii na FreeOnTerminate nie powinna wpływać, bo terminate przecież następuje po zakończeniu wątku i jego pauzowanie/odpauzowanie nie ma sensu, bo on już nie działa... W praktyce to jest VCL i WinAPI, Borland+Microsoft naraz mogli niejedno cudo stworzyć, a ponieważ w temacie, który wskazujesz, sami też cuda wianki wydziwiacie ;P (nie używam wątków w VCL w praktyce, więc całość dyskusji sobie darowałem...) to głęboko wierzę, że to "coś" robi więcej, niż mi się wydaje ;)

0

Nie wiedziałem, że Free() wywołuje jednocześnie destruktor stąd się wzięły moje głupie pytania :-)

Jaka jest różnica (jeżeli jest jakaś) pomiędzy wywoływaniem Free(), a wywoływaniem delete wskaźnik? Czy zawsze powinno się używać Free()?

Czy dobrze rozumiem, że funkcja WaitFor() czeka aż metoda Execute skończy się wykonywać?

Taki problem jeszcze mnie nurtuje:

wywołanie metody Free bądź usunięcie obiektu za pomocą operatora delete zakleszcza program, jeżeli wątek w chwili usuwania jest suspended. Jak w takim razie pozbyć się tego wątku jeżeli nie chcę go uruchamiać?

0

Hmmm, po kolei, chociaż nie na wszystko w sposób zadowalający:

  1. już wiesz :)
  2. zasadniczo, to chyba żadna. Free to metoda destrukcji, którą Builder otrzymał w spadku po Delphi. znając życie, to Free używa delete, albo destruktor podczas niszczenia używa Free.
  3. dobrze
  4. naprawdę, zakleszcza? :> nie wiem, ja nigdy nie usuwałem wątków, dopóki same nie ogłosiły wszem i wobec, że skończyły - ogólnie, to strasznie złe rzeczy chodzą ci po głowie ;) Może ubij taki wątek na chama:
    TerminateThread(ObiektThread->Handle);
    to zadziała na 100%, niczego nie zakleszczy, ale poczytaj w dokumentacji, kiedy z tym uważać, bo możesz trochę stabilność systemu popsuć albo śmieci w pamięci zostawić.
0

Co do niszczenia niezakończonego wątku (TThread), to helpie można znaleźć wyraźną radę by tego unikać. Zalecają by w metodzie Execute sprawdzać cyklicznie wartość Termineted i na tej podstawie zakańczać wykonywanie metody Execute (wątku). Ta wskazówka jest tak wyraźna, że nie ma jasnej metody jak niszczyć wątek przed zakończeniem (dla klasy TThread bo na siłę można zastosować API).

0
MarekR22 napisał(a)

Ta wskazówka jest tak wyraźna, że nie ma jasnej metody jak niszczyć wątek przed zakończeniem (dla klasy TThread bo na siłę można zastosować API).
jest i to bardzo dokładna - trzeba tak oprogramować metodę execute żeby nie trzeba było go ubijać na chama

0

Dzięki za rady.

Ostatnie pytanko: czy można zaimplementować taki trick, że po każdym wywołaniu metody Resume wątek zaczyna działać w konkretnym miejscu Execute, a nie w miejscu w którym został przerwany?

0

zaplanowałem sobie, że za każdym razem gdy użytkownik naciśnie button`a będzie wykonywana metoda Execute (oczywiście za pomocą Resume), niestety wątek zostaje odpalony tylko za pierwszym razem, potem już metoda Resume nie uruchamia Execute. Da się zrobić taki obiekt-wątek wielokrotnego użytku czy może trzeba za...

Poniżej:
Rolę fun Execute spełnia dowolna fun void __fastcall Form1::fun(TObject *Sender)
Można ją uruchamiać wielokrotnie .Form1 + 5xButton + podpiąć odpowiednie ButtonClick .

Wątków nie można zabijać poprzez API w uruchomionym programie,bo to całkowicie
dezorganizuje jego działanie.
Wątek powinien zakończyć się "legalnie" .
Jeśli istnieją co do tego wątpliwości to można sprawdzać stan wątku
i zabezpieczyć się przed próbą usunięcia działającego obiektu

lub zastosować obsługę wyjątków ,pośmiertnie...

Unit1.h

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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//*****************************************************************
class TThreadComponent ;

class TMyThread :public TThread{

                           TThreadComponent* tc ;

                           public:
            void __fastcall Synchro(TThreadMethod &Method);

                           __fastcall TMyThread(bool CreateSuspended,
                           TThreadComponent* tthc);

                           __fastcall ~TMyThread();
                           void __fastcall Execute(void);

                          };
//*****************************************************************
const int THREAD_READY = 0 ;
const int THREAD_WAIT  = 1  ;
const int THREAD_RUN   = 2;
//*****************************************************************

class  TThreadComponent : public TComponent
{
private:
       TMyThread* pMyThread ;
       TNotifyEvent execute ;
       bool bSuspend ;
       bool bRun ;

protected:
public:
        __fastcall TThreadComponent(TComponent* Owner);
        __fastcall ~TThreadComponent();
        void __fastcall Suspend(void);
        void __fastcall Resume(void);
        void __fastcall Execute(void);

 void __fastcall Synchronize(TThreadMethod &Method);
 void __fastcall CreateNewMyThread(void);
  int __fastcall Status(void);
//__published:
__property TNotifyEvent OnExecute = {read=execute ,write=execute
                                                    ,default = 0};
};

//*****************************************************************
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
        TButton *Button1;
        TButton *Button2;
        TButton *Button3;
        TButton *Button4;
        TButton *Button5;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button2Click(TObject *Sender);
        void __fastcall Button3Click(TObject *Sender);
        void __fastcall Button4Click(TObject *Sender);
        void __fastcall Button5Click(TObject *Sender);
private:	// User declarations

       TThreadComponent*pThread ;

void __fastcall ThreadFunc(TObject *Sender);

public:		// User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cpp

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

#include <vcl.h>
#pragma hdrstop

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

//---------------------------------------------------------------------------
//===============    TMyThread     ==========================================
void __fastcall TMyThread::Synchro(TThreadMethod &Method)
{
        Synchronize(Method);
}
//-----------------------------------------------------------------
__fastcall TMyThread::TMyThread(bool CreateSuspended,
                    TThreadComponent* tthc):TThread(CreateSuspended)
{
        tc = tthc ;
}
//-----------------------------------------------------------------
__fastcall TMyThread::~TMyThread()
{
     MessageBox(NULL,"MyThread - KONIEC Wątku","Destruktor",MB_OK);

}
//---------------------------------------------------------------------------
//---------------- posrednie wywolanie wlasciwosci 'execute' przez
//---------------- obiekt TMyThread zawarty w TThreadComponent
//---------------------------------------------------------------------------
void __fastcall TMyThread::Execute(void)
{
    tc->Execute(); 
                          //<---- Wątek kończy się 
    tc->CreateNewMyThread(); // <--- podstawiamy nowy obiekt TThread w TThreadComponent
                                            // poprzedni zostanie usunięty automatycznie
                                           // po zakończeniu Execute 
}
//--------------------------------------------------------------------
//=============== TThreadComponent ==========================================
//---------------------------------------------------------------------------
__fastcall TThreadComponent::TThreadComponent(TComponent* Owner)
        : TComponent(Owner)
{
        CreateNewMyThread();
}
//---------------------------------------------------------------------------
void __fastcall TThreadComponent::Execute(void)
{
      bRun = true ;
      MessageBox(NULL,"Ececute","Execute",MB_OK);
       if(execute)
       {
            execute(this); // Tu jest wywolywana TForm1::ThreadFunc(TObject *Sender)
       }                         // jako osobny wątek
      bRun = false ;
}
//-----------------------------------------------------
void __fastcall TThreadComponent::Synchronize(TThreadMethod &Method)
{
      pMyThread->Synchro(Method);
}
//-----------------------------------------------------------------
void __fastcall TThreadComponent::Suspend(void)
{
      if(!bSuspend)
      {
       pMyThread->Suspend();
       bSuspend = true ;
      }
}
//-----------------------------------------------------------------
void __fastcall TThreadComponent::Resume(void)
{
      if(bSuspend)
      {
       pMyThread->Resume() ;
       bSuspend = false ;
      }
}
//----------------------------------------------------------------
__fastcall TThreadComponent::~TThreadComponent()
{

  MessageBox(NULL,"Destruktor","ThreadComponent",MB_OK);

  if(Status()==THREAD_READY)
  {
     pMyThread ->FreeOnTerminate = false;
     delete pMyThread ;
     pMyThread = NULL ;

  }else{
            throw 0 ;   // zakończenie przy dzialajacym wątku
       }
}
//------------------------------------------------------------------
void __fastcall TThreadComponent::CreateNewMyThread(void)
{
            pMyThread = new TMyThread(true,this);
            pMyThread->FreeOnTerminate = true ;
            bSuspend = true ;
            bRun = false ;
}
//------------------------------------------------------------------
int __fastcall TThreadComponent::Status(void)
{
     if((!bRun)&& bSuspend)
     {
        return THREAD_READY ;
     }
     
     if(bRun && bSuspend )
     {
       return THREAD_WAIT ;
     }

     return THREAD_RUN ;

}
//*****************************************
//=============== Form1 konstruktor ===========================================
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
          // Nowy obiekt Watku
             pThread = new TThreadComponent(0);
             pThread -> OnExecute =  Form1->ThreadFunc ;
//  Captiony dla Guzikow
Button1->Caption = "Resume" ;
Button2->Caption = "Suspend";
Button3->Caption = "ID";
Button4->Caption = "Koniec" ;
Button5->Caption = "Delete" ;
}
//----------------------------------------------------------------------------
//+++++++++++++++ Funkcja wątku ++++++++++++++++++++++++++++++++++++++++++++++
int end ;
//--------
void __fastcall TForm1::ThreadFunc(TObject *Sender)
{
           end = false ;
//  MessageBox bez  Synchronize !
//  To nie jest w 100% prawidlowe .
MessageBox(NULL,"Nowy Wątek",String((int)GetCurrentThreadId()).c_str(),MB_OK);

           while(!end)
           {
             Beep(0x444,100);
             Sleep(200);
           }

}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//----------------------------------------------------------------------------
//-------  Guzik Resume
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   if(pThread->Status()!=THREAD_READY)
   {
       ShowMessage("Wątek jest uruchomiony.");

   }
       pThread->Resume();
}
//---------------------------------------------------------------------------
//-------  Guzik Suspend
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 pThread->Suspend();
}
//---------------------------------------------------------------------------
//-------  Guzik ID - id wątku glownego
void __fastcall TForm1::Button3Click(TObject *Sender)
{
MessageBox(NULL,"Wątek Glowny",String((int)GetCurrentThreadId()).c_str(),MB_OK);
}
//---------------------------------------------------------------------------
//-------  Guzik Koniec Watku
void __fastcall TForm1::Button4Click(TObject *Sender)
{
    if(pThread->Status()==THREAD_WAIT)
    {
       ShowMessage("Wątek dziala i jest zawieszony , wykonaj 'RESUME'.");
    }else{
          end = true ;
          }
}
//---------------------------------------------------------------------------
//-------  Guzik delete
void __fastcall TForm1::Button5Click(TObject *Sender)
{
  // Sprawdz czy można bezpiecznie usunac obiekt
   if(pThread->Status()!=THREAD_READY)  // czy wątek zakonczony
   {                                    // i czeka nowy?
       ShowMessage("Nie można wykonac delete, wątek nie zakonczyl się.");

   }else{
           delete pThread;
           Button5->Enabled = false ; // blok delete
           Button1->Enabled = false ; // blok Resume
           Button2->Enabled = false ; // blok Suspend
        }
}
//---------------------------------------------------------------------------

Ps. Ubicie :
Zastępując kod destruktora przez :

__fastcall TThreadComponent::~TThreadComponent()
{

  MessageBox(NULL,"Destruktor","ThreadComponent",MB_OK);

     pMyThread ->Suspend(); // <--------

     pMyThread ->FreeOnTerminate = false;
     delete pMyThread ;
     pMyThread = NULL ;

}

Można "Ubić" wątek w czasie jego wykonywania się i pominąć
sprawdzenia .

Można też zabić wątek bez usuwania podstawowego obiektu ,
stary wątek jest usuwany - delete i w jego miejsce
podstawiany nowy , oczekujący na wykonanie np.:

void __fastcall TThreadComponent::Break(void)
{
            // Ubicie wątku
     pMyThread ->Suspend();
     pMyThread ->FreeOnTerminate = false;

     delete pMyThread ;

     pMyThread = NULL ;

     CreateNewMyThread();
                             // zastąpienie nowym w stanie zawieszenia
      /* CreateNewMyThread() ==[ pMyThread = new MyThread(Zawieszony==true) ]*/

}
.....
pThread->Break() ; // wywalamy wątek i bierzemy następny
....

Usuwamy normalnie .

delete pThread;

Ten kod działa powodując zabicie wątku z poprawnym wykonaniem
destruktora TThread bez załamania aplikacji i użycia bezpośrednio funkcji API
,ale czy jest do końca bezpieczny w użyciu to wymaga jeszcze przetestowania .
Fun API zabijające wątki pozostawiają stos funkcji wątku aby
nie narobić bałaganu oraz nie niszczą obiektów C++ utworzonych
w niszczonym wątku .
Gdyby kogoś to interesowało
dalsze rozważania , komponent + kod oparty na powyższych fragmentach będą dostępne
(od 01.03.2008) pod adresem:
http://www.win32prog.republika.pl/winapp/tcthelp.html
w 1 wersji testowej dla samobójców i ryzykantów.. [green] .

a nie w miejscu w którym został przerwany?

Pewnie tak ale będzie to wymagało kosmicznych wygibasów...

1 użytkowników online, w tym zalogowanych: 0, gości: 1