VCL Jak z wątku pobocznego wymusić zdarzenie w wątku głównym

0

Piszę program w C++ Builder i mam następujący problem:
mam pewien własny komponent, a w nim wątek (stworzony i obsługiwany obiektem typu TThread z biblioteki VCL) i chcę z z tego wątku "wystrzeliwać" pewne zdarzenia, które muszą być wykonywane w głównym wątku (by przyszły użytkownik mógł wykonywać operacje graficzne przy tym zdarzeniu i nie był świadom wielowątkowości).

Co do zdarzenia to wymagam, by wątek na czas jego wykonywania nie był zawieszany, więc zastosowanie metody TThread::Synchronize jest niewskazane.

W tej chwili wykorzystuje zdarzenie TThread::OnTerminate, które w wątku jest wywoływanie wielokrotnie przez TThread::DoTerminate(). Jednak to rozwiązanie jest niewygodne bo mam więcej zdarzeń innego rodzaju i "przepychanie" ich przez jedno zdarzenie jest niewygodne (szczególnie jeśli trzeba jeszcze przesłać jakieś argumenty).

Będę wdzięczny za wskazanie mi innych rozwiązań bardziej klarownych i profesjonalnych. Przypominam chodzi o to by z wątku pobocznym spowodować pojawienie się zdarzenia w wątku głównym programu. Najlepiej gdyby rozwiązanie było VCL-owe (API znam bardzo słabo, więc to jest ostateczność).
Najlepsze byłoby rozwiązanie w stylu BeginInvoke z .Net-a.

Na tutejszym forum znalazłem podobny temat, ale brak tam właściwej odpowiedzi. ;-(

0

w wątku głównym programu

Czyli gdzie [?] , aplikacja VCL nie określa specjalnie swojego "wątku" głównego
jako miejsca w programie gdzie można umieścić jakiś kod ,
chyba że tworzysz jakiś wątek i traktujesz go jako główny lub używasz pętli .
Ewent jako wątek można wykorzystać OnIdle aplikacji .

wątku pobocznym spowodować pojawienie się zdarzenia w wątku głównym programu

"Wystrzelenie zdarzenia " [?] , wysłać komunikat WM_USER+n do okna głównego z wątku podrzędnego,
obsłużyć komunikat ( wywołana zostanie Fu. w wątku głównym aplikacji ) .
...

0
dzejo napisał(a)

aplikacja VCL nie określa specjalnie swojego "wątku" głównego
jako miejsca w programie gdzie można umieścić jakiś kod
Odsyłam do helpa np TThread::Synchronize

Help napisał(a)

Description
Synchronize causes the call specified by Method to be executed using the main VCL thread, thereby avoiding multi-thread conflicts. If you are unsure whether a method call is thread-safe, call it from within the Synchronize method to ensure that it executes in the main VCL thread.

dzejo napisał(a)

Ewent jako wątek można wykorzystać OnIdle aplikacji .
??? [???]
Z tego co wiem zdarzenie OnIdle wywoływane jest gdy kolejka komunikatów jest pusta (znowu dosyłam do helpa). Zresztą Idle oznacza po angielsku bezczynny. No chyba, że chodzi ci zupełnie o co innego, ale wtedy to oznacza, że nie opisałeś tego w zrozumiały sposób.

dzejo napisał(a)

wysłać komunikat WM_USER+n do okna głównego z wątku podrzędnego,
obsłużyć komunikat ( wywołana zostanie Fu. w wątku głównym aplikacji ) .
...
Fajnie, ale napisałem, że nie znam się za bardzo na API, więc przydało by się trochę kodu bym mógł zakumać co i jak.

Dzięki za chęci, ale walnąłeś parę haseł, z których w zasadzie dla mnie nic nie wynika :-/ .

0

Nie wynika , nie wynika ...
To się trochę zainteresuj jak to wszystko działa , odcinanie sie od API nie pomoże
jeśli chcesz uzyskać kod o którym piszesz trzeba mieć przynajmniej podstawy
zasady działania aplikacji w Windows + API [ komunikaty i obsługa ]
VCL zawiera własne rozwiązanie w postaci dostępu do WndProc za pomocą makr
BEGIN_MESSAGE_MAP i END_MESSAGE_MAP . [ a WM_USER jest zastąpione przez WM_APP]
W Builderze powinien być przykład w Katalogu instalacyjnym /Examples/Apps/MsgMap

OnIdle wywoływane jest gdy kolejka komunikatów jest pusta

I o to chodzi , inny wątek może ustawić np zmienną globalną której stan spowoduje wywołanie
funkcji zawartej w OnIdle .

Komunikaty:

przydało by się trochę kodu

Poszukaj na google lub na cyfbar.republika.pl , podrzucę własny jeśli będą problemy ale troszkę później ...
No i zajrzyj do Helpa ;-) -> overriding message handlers

/--/
//EDIT
Obsługa komunikatów w C++ Builder [ przykład składa sie z Form1 i jednego Buttona] :

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//====================================
// własny komunikat

#define WM_MOJKOMUNIKAT WM_APP+100
//======================================
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
private:	// User declarations
public:		// User declarations
    __fastcall TForm1(TComponent* Owner);

void virtual __fastcall Fun_ObslugaKomunikatu(TMessage& Msg);
//_________________________________________________________
BEGIN_MESSAGE_MAP

MESSAGE_HANDLER(WM_MOJKOMUNIKAT,TMessage,Fun_ObslugaKomunikatu)

END_MESSAGE_MAP(TForm)
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif


//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void  __fastcall TForm1::Fun_ObslugaKomunikatu(TMessage& Msg)
{
   ShowMessage("Aplikacja odebrała komunikat WM_MOJKOMUNIKAT");

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


void __fastcall TForm1::Button1Click(TObject *Sender)
{
   Form1->Perform(WM_MOJKOMUNIKAT,0,0);
   // lub zastosować fun API SendMessage
}
//---------------------------------------------------------------------------

Kod z Buttona wystarczy wywołać z wątku , spowoduje to uruchomienie funkcji Fun_ObslugaKomunikatu(TMessage& Msg)

0
MarekR22 napisał(a)

... pewne zdarzenia, które muszą być wykonywane w głównym wątku ... by wątek na czas jego wykonywania nie był zawieszany, więc zastosowanie metody TThread::Synchronize jest niewskazane.
albo jedno albo drugie - nie można zjeść jabłka i mieć jabłko

W tej chwili wykorzystuje zdarzenie TThread::OnTerminate, które w wątku jest wywoływanie wielokrotnie
tu jakieś bajki opowiadasz - OnTerminate jest wywoływane RAZ po zakończeniu metody Execute wątku

Pomijając fakt, że tak nieszczególnie łapę co chcesz zrobić to jednak część z Twoich założeń jest sprzeczna ze sobą

0
Misiekd napisał(a)
MarekR22 napisał(a)

... pewne zdarzenia, które muszą być wykonywane w głównym wątku ... by wątek na czas jego wykonywania nie był zawieszany, więc zastosowanie metody TThread::Synchronize jest niewskazane.
albo jedno albo drugie - nie można zjeść jabłka i mieć jabłko.
Ja chcę miecz zdarzenie asynchroniczne i tyle, gdzie tu jest sprzeczność? Tak jak napisałem w C# załatwia się to za pomocą BeginInvoke lub synchronicznie przez Invoke jednym prostym zapisem z delegatami.

Misiekd napisał(a)
MarekR22 napisał(a)

W tej chwili wykorzystuje zdarzenie TThread::OnTerminate, które w wątku jest wywoływanie wielokrotnie
tu jakieś bajki opowiadasz - OnTerminate jest wywoływane RAZ po zakończeniu metody Execute wątku
Tu nie mogę się z tobą zgodzić. W klasie TThread jest metoda TThread::DoTerminate(), która wymusza pojawienie się tego zdarzenia niezależnie od tego czy watek się skończył czy nie (odsyłam do helpa). Poza tym już wiele razy to wykorzystywałem i to działa jak powinno.

0

@dzejo

  1. Chcę wymusić pojawianie się zdarzenia w sposób asynchroniczny, więc Form1->Perform odpada i SendMessage (API) też. Teoretycznie powinienem skorzystać z PostMessage (API) bo on wysyła komunikat asynchroniczne, ...
  2. Tyle, że ja mam komponent niewizualny, który będzie umieszczany na formie i podobnie jak Timer komponent ten nie ma uchwytu (Handle), więc nie wiem za bardzo jak wywołać PostMessage bez pierwszego argumentu :(.
  3. Z tego co zrozumiałem to BEGIN_MESSAGE_MAP i MESSAGE_HANDLER etc. umieszcza się w deklaracji formy, a ja jej w komponencie nie mam. (swoją drogą chyba muszę doczytać o tworzeniu komponentów, może coś znajdę).
0

asynchronicznosc zdarzen wymaga z definicji, aby "CEL" na ktory ma je asynch. wykonac posiadal (niekoniecznie wlasna) jakas kolejke zdarzen i watek wyonywania.. jesli ow cel nie jest identyfikowalny (nie ma HANDLE'a) to raczej nie jest podpiety pod zadna istniejaca kolejke. w takim razie musialbys zaopatrzyc go we wlasny system kolejkowania wiadomosci i wlasny watek roboczy.. afaik, chyba lepiej uczynic go zwyklym komponentem ze zwykla obsluga wiadomosci, tylko bez wizualizacji.. w C# wlasnie tak to jest fizycznie robione -- poprzez handle i postmessage

0
MarekR22 napisał(a)

Ja chcę miecz zdarzenie asynchroniczne i tyle, gdzie tu jest sprzeczność?
sprzeczność tu jest w samym Delphi - nie chcesz synchronize - ok, nie masz uchwytu do "czegoś" co ma tą metodę wykonać - ok, ale nie miej pretensji że nie da się wykonać wszystkiego tak jak Ty chcesz tylko muszą być spełnione pewne warunki.

Tak jak napisałem w C# załatwia się to za pomocą BeginInvoke lub synchronicznie przez Invoke jednym prostym zapisem z delegatami.
Uwierz, ja też po przesiadce na C# widzę jak dużo brakuje Delphi (chociaż może nie tyle brakuje co po prostu trzeba wymyślać różne dziwnbe konstrukcje, a w c# da się to jednym poleceniem.)

Tu nie mogę się z tobą zgodzić. W klasie TThread jest metoda TThread::DoTerminate(), która wymusza pojawienie się tego zdarzenia niezależnie od tego czy watek się skończył czy nie (odsyłam do helpa). Poza tym już wiele razy to wykorzystywałem i to działa jak powinno.
ale to jest metoda, nazwijmy ją wewnętrzna wątku i nie powinno się jej tak używać bo jest ona do czegoś innego. Chcesz mieć własne zdarzenia wywoływane z wątku - dopisz je sobie, nikt Ci nie broni i nic nie stoi na przeszkodzie. Co więcej działają i to bardzo dobrze, nie ma problemu z tym co przekazują bo nic Cię nie ogranicza - w końcu to Twoje metody, ale Terminate służy do informowania o ZAKOŃCZENIU się wątku i tylko do tego! Jak ktoś po Tobie (lub nawet Ty po jakimś czasie) będziesz poprawiał ten kod to będzie dopiero jazda

0

@Misiekd: masz 100% racji, ale skoro TThread ma zdarzenie OnTerminate, które można wywołać używając DoTerminate to musi być jakiś mechanizm by do tej klasy dorobić sobie więcej zdarzeń (skoro jest jeden to może być więcej). Dokładnie z tych powodów co opisałeś chcę stworzyć dodatkowe zdarzenia, bo w tej chwili nie widzę innego prostego sposobu jak zrobić kolejkę swoich zdarzeń, którą przetwarzałbym w OnTerminate.

0

Tyle, że ja mam komponent niewizualny, który będzie umieszczany na formie i podobnie jak Timer
...

Nie ma znaczenia.
Jak umieścisz komponent na Formie to masz już Formę i wskaźnik do niej .
Wystarczy że komponent otrzyma ten wskaźnik i będzie wysyłał komunikat .
Może też dla funkcji API otrzymać tylko uchwyt HWND okna głównego ,
wtedy wysyłasz SendMessage lub PostMessage podając jako pierwszy parametr
uchwyt Okna Form .

Nie widzę tu żadnego problemu .
Twój komponent ma tylko wywołać jakąś swoją procedurą Send/PostMessage ze zdefiniowanym
komunikatem i uchwytem Okna głównego .

Nie bardzo rozumiem w czym przeszkadza to że Mapa Komunikatów jest definiowana w Form ...
Komponent nie ma tu nic do rzeczy .

Ps. Jeśli chcesz wbudować "Zdarzenie" w swój komponent [ coś na wzór OnTimer ,TTimer]
to powinieneś do sekcji __published dodać właściwość __property TNotifyEvent <- Event Handler ,
właściwość ma być powiązana z prywatnym wskażnikiem komponentu TNotifyEvent*_nazwa ;
właściwość ta pojawi się w inspektorze obiektów jako opublikowana na zakładce Events.
Można też definiować własne typy zdarzeń (wskaźniki na fun o innym prototypie).
Temat ,Tworzenie komponentów.

Czyli co ? Budujesz komponent i chcesz mieć funkcje zdarzeniowe widoczne w Inspektorze
obiektów , i te funkcje (OnCośTam) mają być uruchamiane z twojego komponentu , dobrze kombinuje ?

0

@dziejo
Na to rozwiązanie też wpadłem, ale ma ono jedną wadę. Jeśli ktoś będzie chciał skorzystać z mojego zdarzenia to zamiast stosować proste podstawienie: OnSomething = Form1->SmoethingExecute, będzie musiał zastosować MessageMap etc.
Lepiej byłoby utworzyć jakiś obiekt z uchwytem z własnym przetwarzaniem komunikatów, który łapałby mój Message, a następnie wywoływał odpowiednie zdarzenie OnSomething. W ten sposób ukryłbym cały mechanizm komunikatów przed przyszłym użytkownikiem. Niestety nie wiem jak za to się zabrać. Co dziwne takiego rozwiązania spodziewałbym się pod innych komponentach niewizualnych jak np Timer, ale on nie ma własnego uchwytu (przynajmniej nie jest on dostępny), więc wygląda na to, że można jeszcze inaczej.

0

Hym, coraz mniej z tego rozumiem , ale niech tam :-D .
Tworząc nowy komponent możesz dziedziczyć po klasie TWinControl ,
klasa ma Handle i własną procedurę WndProc .
Na jej podstawie można utworzyć komponent który może posiadać MESSAGE_MAP
, odbierać i wysyłać komunikaty , komponent może byc niewidoczny .

0

Wielkie dzięki wszystkim, naprowadziliście mnie na miejsce gdzie tego szukać. Przykładowe rozwiązanie problemu zamieszczam poniżej:

<font size="2">Jak do klasy TThread dołączyć więcej zdarzeń:</span>
Plik nagłówkowy:

//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <SyncObjs.hpp>
//---------------------------------------------------------------------------
class TTestowyKominkatywny : public TThread
{            
private:
    HWND FWindowHandle;
    TNotifyEvent started;
    TNotifyEvent progress;
    TCriticalSection * protect;
    void __fastcall SetOnProgress(TNotifyEvent value);
    TNotifyEvent __fastcall GetOnProgress();
    void __fastcall SetOnStarted(TNotifyEvent value);
    TNotifyEvent __fastcall GetOnStarted();

protected:
    void __fastcall Execute();
    void __fastcall DoStarted();
    void DoProgress();
    void __fastcall WndProc(TMessage & Message);
public:
    __fastcall TTestowyKominkatywny(bool CreateSuspended);
    virtual __fastcall~TTestowyKominkatywny();
    __property TNotifyEvent OnProgress  = { read=GetOnProgress, write=SetOnProgress, default=0 };
    __property TNotifyEvent OnStarted  = { read=GetOnStarted, write=SetOnStarted, default=0 };
};
//---------------------------------------------------------------------------
#endif

Plik z kodem (wątek robi coś bezsensu, ale to nie jest istotne):

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit2.h"
#pragma package(smart_init)

#define WM_STARTED WM_APP+100
#define WM_PROGRESS WM_APP+101
//---------------------------------------------------------------------------

__fastcall TTestowyKominkatywny::TTestowyKominkatywny(bool CreateSuspended)
    : TThread(CreateSuspended)
{
    FWindowHandle = 0;
    FWindowHandle = AllocateHWnd(&WndProc);
    protect = new TCriticalSection;
}
//---------------------------------------------------------------------------
void __fastcall TTestowyKominkatywny::Execute()
{
    DoStarted();
    do
    {
        Sleep(1000);
        DoProgress();
        Sleep(500);
        Beep();
    }
    while(!Terminated);
}
//---------------------------------------------------------------------------


void __fastcall TTestowyKominkatywny::DoStarted()
{
    PostMessage(FWindowHandle,WM_STARTED,0,0);
}            

void TTestowyKominkatywny::DoProgress()
{
    PostMessage(FWindowHandle,WM_PROGRESS,0,0);
}

void __fastcall TTestowyKominkatywny::WndProc(TMessage & Message)
{
    TNotifyEvent ev;

    ev = 0;
    switch(Message.Msg)
    {
    case WM_STARTED:
        ev = OnStarted;
        break;
    case WM_PROGRESS:
        ev = OnProgress;
        break;
    default:
        Dispatch(&Message);
        return;
    }
    if(ev)
        ev(this);
}

__fastcall TTestowyKominkatywny::~TTestowyKominkatywny()
{
    if(FWindowHandle)
        DeallocateHWnd(FWindowHandle);    
    if(protect)
        delete protect;
}

void __fastcall TTestowyKominkatywny::SetOnProgress(TNotifyEvent value)
{
    protect->Enter();
    try
    {
        progress = value;
    }
    __finally
    { protect->Leave(); }
}
TNotifyEvent __fastcall TTestowyKominkatywny::GetOnProgress()
{
    TNotifyEvent ev;
    protect->Enter();
    try
    {
        ev =  progress;
    }
    __finally
    { protect->Leave(); }
    return ev;
}

void __fastcall TTestowyKominkatywny::SetOnStarted(TNotifyEvent value)
{
    protect->Enter();
    try
    {
        started = value;
    }
    __finally
    { protect->Leave(); }
}
TNotifyEvent __fastcall TTestowyKominkatywny::GetOnStarted()
{
    TNotifyEvent ev;
    protect->Enter();
    try
    {
        ev =  started;
    }
    __finally
    { protect->Leave(); }
    return ev;
}

Będę wdzięczny jeśli ktoś wyłowi jakieś błędy (szybko napisana aplikacja testowa przy zakończeniu zgłasza dostęp do zerowej komórki pamięci, reszta działa jak trzeba). Sprawdziłem debugerem, komunikaty wywoływane są w głównym wątku :).

0

Nie jest, aż tak dobrze [glowa].
Po zniszczeniu obiektu mojego wątku, przełączenie na inną aplikację lub zakończenie aplikacji powoduje pojawienie się wyjątku "Access violation at address 00000000. Read of adress 00000000." Co dziwne jeśli nie zrobię jak Pan Bóg przykazał i nie zwolnię tego obiektu program kończy się prawidłowo.
Czy ktoś widzi gdzie jest błąd? Gdzie zwalniam za dużo lub za mało zasobów? A może zapomniałem o wywołaniu jakieś metody przed zwolnieniem uchwytu. W każdym razie przy okazji w Helpie znalazłem przykład jak zrobiony jest TTimer odnośnik Example przy temacie DeallocateHwnd i nie widzę większych różnic z moim kodem :-[ [!!!] .

A to dobre uzupełnienie początku destruktora o linijki:

    Terminate();
    if(Suspended)
        Resume();
    WaitFor();

załatwiło sprawę i teraz wszystko jest Ok :-D ;-P . Ten kod jest konieczny pomimo, że niszczyłem obiekt przez ustawienie właściwości FreeOnTerminate=true.

0

AllocateHWnd(&WndProc); // ?

AllocateHWnd(WndProc); :-)

0

Racja powinno być bez "&", ale i tak obie wersje działają :|

0

który będzie umieszczany na formie i podobnie jak Timer
...

Komponent aby mógł być zainstalowany w palecie komponentów dziedziczy po klasie
TComponent .[Component ->Install Component] .

Szkielet , do kompletu trzeba dodać możliwość zawieszania wątku , wznawiania
i usunięcia w przypadku zniszczenia obiektu .

//---------------------------------------------------------------------------
// C++ Builder 6

#ifndef CrossThreadH
#define CrossThreadH
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>

//                          CrossThread.h
//---------------------------------------------------------------------------
#include <syncobjs.hpp>

#define WM_STARTED WM_APP+100
#define WM_PROGRESS WM_APP+101

int __fastcall Execute(void*p);
TCriticalSection * protect;

//---------------------------------------------------------------------------
class PACKAGE CrossThread : public TComponent
{
private:
    HWND FWindowHandle;

    TNotifyEvent started;
    TNotifyEvent progress;
    int hThread ;
    unsigned  idThread ;
    bool start ;

protected:

    void __fastcall WndProc(TMessage & Message);
public:
    TNotifyEvent ev ;
    void __fastcall DoStarted();
    void __fastcall DoProgress();
    void __fastcall Start(void);

    __fastcall CrossThread(TComponent* Owner);

    virtual __fastcall~CrossThread();

__published:
    __property TNotifyEvent OnProgress = { read=progress, write=progress,default=0};
    __property TNotifyEvent OnStarted  = { read=started, write=started,default=0};
    __property TNotifyEvent OnExecute  = { read=ev, write=ev,default=0};
};
//---------------------------------------------------------------------------
#endif
 
//***************************************************************************

//                                CrossThread.cpp

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

#include <basepch.h>

#pragma hdrstop

#include "CrossThread.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(CrossThread *)
{
        new CrossThread(NULL);
}
//---------------------------------------------------------------------------
__fastcall CrossThread::CrossThread(TComponent* Owner)
        : TComponent(Owner)
{
    FWindowHandle = AllocateHWnd(WndProc);

    protect = new TCriticalSection;

    start = true ;
}
//---------------------------------------------------------------------------
void __fastcall CrossThread::Start(void)
{
   if(start)
   {
   hThread = BeginThread(NULL,0,Execute,this,0,idThread);
      start = false ;
   }
}
//---------------------------------------------------------------------------
int __fastcall Execute(void*p)
{
   if(p)
   {
    protect->Enter();
    try
    {
    ((CrossThread*)p)->ev((System::TObject*)p);
    }
    __finally
    { protect->Leave(); }
   }
    return 1 ;
}
//---------------------------------------------------------------------------
__fastcall CrossThread::~CrossThread()
{

    if(FWindowHandle)
    {
        DeallocateHWnd(FWindowHandle);
    }
    if(protect)
    {
        delete protect;
    }

}
//---------------------------------------------------------------------------
void __fastcall CrossThread::WndProc(TMessage & Message)
{
    TNotifyEvent ev;

    ev = 0;
    switch(Message.Msg)
    {
    case WM_STARTED:
        ev = OnStarted;
        break;
    case WM_PROGRESS:
        ev = OnProgress;
        break;
    default:
        Dispatch(&Message);
        return;
    }
    if(ev)
    {
        ev(this);
    }
}
//---------------------------------------------------------------------------
void __fastcall CrossThread::DoStarted()
{
    PostMessage(FWindowHandle,WM_STARTED,0,0);
}
//---------------------------------------------------------------------------
void __fastcall CrossThread::DoProgress()
{
    PostMessage(FWindowHandle,WM_PROGRESS,0,0);
}
//---------------------------------------------------------------------------
namespace Crossthread
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(CrossThread)};
                 RegisterComponents("Samples", classes, 0);
        }
}
//---------------------------------------------------------------------------

Zdarzenie OnExecute jest wywoływane w oddzielnym wątku.
Zdarzenia OnProgress i OnStarted w wątku głównym poprzez DoProgress i DoStarted.

Przykład :

void __fastcall TForm1::CrossThread1Execute(TObject *Sender)
{
      MessageBox(NULL,"Thread",String((int)GetCurrentThreadId()).c_str(),MB_OK);

      Sleep(1000);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    CrossThread1->Start();
    CrossThread1->DoProgress();
            
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CrossThread1Progress(TObject *Sender)
{
      MessageBox(NULL,"Thread",String((int)GetCurrentThreadId()).c_str(),MB_OK);
}
//---------------------------------------------------------------------------
0

mozna jeszcze w watku w chwili gdy sie chce wywolac zdarzenie, skopiowac potrzebne dane do zdarzenia a nastepnie utworzyc nowy watek i w nim wykonac zdarzenie, nie zatrzymujac wlasnego watku (i nie sa potrzebne uchwyty) .

0

Nie nie można , ma być super asynchronicznie ... żartowałem.. ;-)
Swoją drogą takie mieszanie "między wątkowe" może zniszczyć program , ciężko
załapać co z czym zsynchronizować i gdzie (klasy + rozszerzenia Delfiowe + API)= ryzyko .

Ciekawe to czego może służyć takie coś ?

0

che che dobre pytanie co to jest i do czego ...

Raczej nie zniszczy

  1. watek wazny (nazwijmy go tak) ma potrzebe wygenerowac zdarzenie
  2. przekazuje dane (zakladamy ze tylko do odczytu) do nowego watku, ktory tworzy
    3) watek poboczny wywoluje zdarzenie (poatrzebna tylko synchronizacja z tym co ma zdarzenie)
    4) watek sie konczy
  3. watek glowny caly czas pracuje, nawet jak ktos da MessageBox w zdarzeniu

gorzej jest gdy dane sa do odczytu zapisu ... to raczej ciezka sprawa ...

0

watek glowny caly czas pracuje, nawet jak ktos da MessageBox w zdarzeniu

Ale jak da się w zdarzeniu opublikowanym przez inspektora obiektów
widocznym w kodzie 'niby to w wątku głównym' wywoływanym przez wątek
[poboczny ? BeginThread<-VCL iAPI ( Execute.<- API[nie] <- globalna funkcja dla wątku 2 , this <-klasa VCL )
(CrossThread)
funkcję 'ShowMessage' to sie VCL wywala , dlatego dałem MessageBox [green] ...
Z ShowMessage Exception okrutny generuje odnośnie Canvas ,
co ciekave operacje na Form przebiegają bez problemu w tym z dostępem do Canvas TForm.
Czyli kod typu :

void __fastcall TForm1::CrossThread1Execute(TObject *Sender)
{
    //  MessageBox(NULL,"Thread",String((int)GetCurrentThreadId()).c_str(),MB_OK);
        ShowMessage(String((int)GetCurrentThreadId())) ;

      Sleep(1000);

       /*   To jest  wywołanie z innego wątku przez CrossThread  jako fun podpięta do zdarzenia 
              OnExecute */

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    CrossThread1->Start();
    CrossThread1->DoProgress();
            
}
//---------------------------------------------------------------------------
void __fastcall TForm1::CrossThread1Progress(TObject *Sender)
{
      MessageBox(NULL,"Thread",String((int)GetCurrentThreadId()).c_str(),MB_OK);
}

kończy się śmiercią programu .
I co tu z czym synchroniowac to nie mam pojęcia , wygląda na nierozwikłany konflikt interesów.. :-P

Ps. Dlaczego Borland nie umieścił komponentu TThread na palecie komponentów tak jak TTimer ?

0

Kopsnij gdzies kod w calosci (bez srodka same watki) to popatrze. Najlepiej jakiegos zipa na rapidshare albo cos takiego.

Nie umiescil bo raczej sie obudowuje do wlasnych potrzeb klase TThread.

0

(bez srodka same watki)

? , wątki są 'zintegrowane' z komponentem który można zainstalować w palecie na zakładce Samples
Kod jest podany wcześniej + sposób użycia , CrossThread.cpp i CrossThread.h , można to zainstalować w palecie komponentów C++ Builder [ używałem bcb 6 ] wskazując plik żródłowy .
Instalujemy to w nowym pakiecie .
Następnie otwieramy pakiet jako nowy projekt komilujemy [ewent zmiany] i install .
Ma się zainstalować w zakładce Samples .

Instalacja może przebiegać różnie w zależności od wer. bcb .
Jeśli nie pójdzie na tych plikach (nagłówki) to trzeba komponent utworzyć od początku
wklejając kod i zostawiając pliki nagłówkowe speceficzne dla danej wersji .
Tzn utworzyć nowy komponent o nazwie CrossThread na bazie TComponent i wkleić kod .
Mam jeszcze bcb 3 , w razie problemów mogę sprawdzić na 3 .

Po umieszczeniu na formularzu komponent posiada widoczne właściwości :
OnExecute , OnProgress , OnStarted ,

Wątek 2:
OnExecute jest wywoływane po użyciu funkcji komponentu nazwa->Start() .
Tworzony jest wątek który wywołuje fun powiązaną z OnExecute .
Uwaga! .
Na potrzeby testu komponent zawiera zmienną bool która nie pozwala uruchomić ponownie
wątku po użyciu funkcji Start() . Jeśli OnExecute nie będzie zawierać pętli wątek zakończy
się po jego wywołaniu funkcją Start() i kolejne użycie Start() nie będzie uruchamiać nowego wątku .

Kod jest maksymalnie uproszczony i nie pozwala na zawieszanie - wznawianie wątku lub ponowne
utworzenie w przypadku zakończenia wywołania OnExecute .

Wątek główny programu .
OnProgress jest wywoływane po użyciu funkcji komponentu DoProgreess() .
OnStarted jest wywoływane po użyciu funkcji komponentu DoStarted() .

0

dobra podaj calosc w pliku na mejla (reichel at rudy.mif.pg.gda.pl) bo mi sie nie chce tworzyc tego ...

chodzilo "mi bez srodka" tzn bez innych czesci programu, a tylko sam program z problemem.

0

Proszę bardzo , lecz muszę najpierw ładnie wszystko poukładać i sprawdzić jeszcze na bcb 3
i dwóch komputerach , wkrótce prześlę ...

//
//Edit poszło
Wątki 4p - dzejo sobota, 17:19 +
Wątki --EDIT- 4p-dzejo sobota, 17:24

0

Co ciekawe u mnie dzialaja obie wersje ?!

Jak widze to ma byc cos podobnego jak backgroundworker z .NET, ale w jego watku i tak trzeba stosowac delegaty jak sie odwoluje do formy, wiec niestety tu tez (ale synchronize).

0

Co ciekawe u mnie dzialaja obie wersje ?!

U mnie różnie , co pewnie zależy od stanu wątków , czyli
Synchronize jak napisałeś trzeba .

Odświerzyłem porzucony kod "Wizualnego" komponentu TThread .
Zawiera metodę Synchronize i działa ok , jeśli wywołania
ShowMessage wstawi się w funkcję wołaną przez Synchronize .
Komponent można zainstalować w palecie BCB .

Udostępnia metody :
Resume() , wznawianie
Suspend() , zawieszanie
Synchronize(...) identyczne z TThread::Synchronize .
Udostępnia zdarzenie
OnExecute , działające w nowym wątku .

Po utworzeniu obiekt ( new lub umieszczenie na formularzu ) tworzy nowy wątek reprezentowany przez OnExecute .
Wątek jest w stanie Zawieszenia .

//EDIT kod nie kompletny , brak prawidłowego zwalniania obiektu

ThreadComponent.h:

//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.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);

                          };
//---------------------------------------------------------------------------
class PACKAGE TThreadComponent : public TComponent
{
private:
       TMyThread* pMyThread ;
       TNotifyEvent execute ;

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);
__published:
__property TNotifyEvent OnExecute = {read=execute ,write=execute
                                                    ,default = 0};
};
//---------------------------------------------------------------------------
#endif

ThreadComponent.cpp:

#include <basepch.h>

#pragma hdrstop

#include "ThreadComponent.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TThreadComponent *)
{
        new TThreadComponent(NULL);
}
//---------------------------------------------------------------------------
__fastcall TThreadComponent::TThreadComponent(TComponent* Owner)
        : TComponent(Owner)
{
            pMyThread = new TMyThread(true,this);
           //  pMyThread->FreeOnTerminate = true ;  @ POST  03-02-2008 19:00
}
//---------------------------------------------------------------------------
void __fastcall TThreadComponent::Execute(void)
{
       if(execute)
       {
            execute(this);
       }
}
//-----------------------------------------------------
void __fastcall TThreadComponent::Synchronize(TThreadMethod &Method)
{
      pMyThread->Synchro(Method);
}
//-----------------------------------------------------------------
void __fastcall TThreadComponent::Suspend(void)
{
       pMyThread->Suspend();
}
//-----------------------------------------------------------------
void __fastcall TThreadComponent::Resume(void)
{
       pMyThread->Resume() ;
}
//-----------------------------------------------------------------
void __fastcall TMyThread::Synchro(TThreadMethod &Method)
{
        Synchronize(Method);
}
//-----------------------------------------------------------------
__fastcall TMyThread::TMyThread(bool CreateSuspended,
                        TThreadComponent* tthc):TThread(CreateSuspended)
{
        tc = tthc ;
}
//-----------------------------------------------------------------
__fastcall TMyThread::~TMyThread()
{
     ;
}
//------------------------------------------------------------------
__fastcall TThreadComponent::~TThreadComponent()
{
     //  pMyThread->Terminate(); @ POST  03-02-2008 19:00

}
//---------------------------------------------------------------------------
//---------------- posrednie wywolanie wlasciwosci 'execute' przez
//---------------- obiekt TMyThread zawarty w TThreadComponent
//----------------------------------------------------------------------------
void __fastcall TMyThread::Execute(void)
{

    tc->Execute();
}
//---------------------------------------------------------------------------
namespace Threadcomponent
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TThreadComponent)};
                 RegisterComponents("Samples", classes, 0);
        }
}
//---------------------------------------------------------------------------

Pytania :
1.
Tylko mam wątpliwości co do destruktorów .
Klasa główna traktowana jako Komponent to 'ThreadComponent'
zawiera ona obiekt "wątkowy" po TThread (TMyThread) tworzony dynamicznie 'new' i z ustawianym parametrem FreeOnTerminate = true ;.

W destruktorze 'ThreadComponent' wywoływana jest jedynie metoda Terminate() na rzecz obiektu TMyThread , co podobno ma zakończyć watek oraz zwolnić zasoby TMyThread .

Destruktor TMyThread jest pusty .

Czy to jest prawidłowo zrobione jeśli chodzi o bezpieczne usunięcie wątku ?

Czy nie trzeba użyć jakiegoś tam WaitFor a jeśli tak to gdzie ?

0

Ad 1. Metoda Terminate() nie powoduje zakończenia wątku, a jedynie ustawia właściwość TThread::Terminated na true! Wątek powinien cyklicznie sprawdzać tą właściwość by upewnić się, że powinien działać dalej, a ja tu takiego mechanizmu nie widzę, a nawet przygotowania do niego też nie ma.
Co do destruktorów to też mam wątpliwości czy to będzie zawsze działać dobrze.
Moim zdaniem cała koncepcja tworzenia takiego komponentu jest błędna (dlatego nie ma analoga tego komponentu w standardowych bibliotekach Buildera i Delphi oraz innych językach). Sens robienia tego typu komponentów jest tylko w momencie specjalizacji (np komunikacja z jakimś urządzeniem), gdzie wątek obsługuje specjalne czynności.

0

E.. , no tak , czyli kicha .
Znowu ustawione FreeOnTerminate = true , powoduje automatyczne zniszczenie obiektu
po zakończeniu wątku , czyli też źle to wymyśliłem .Wątek mi zniknie , zniszczy
TThread i pozostanie uszkodzony obiekt zawierający "kiedyś" TThread ... hym .
Do przerobienia... niedokładnie przeczytałem helpa [rotfl] .

Moim zdaniem cała koncepcja tworzenia takiego komponentu jest błędna
Może i jest , postanowiłem to sprawdzić doświadczalnie ... [green]

/-/

Ps. Chyba wiem już jak rozwiązać problem zwalniania :
FreeOnTerminate = true ;
jeśli wątek się zakończy , kolejne wywołanie Resume() Komponentu powinno utworzyć
nowy obiekt TThread [TMyThread] w komponencie , w ten sposób OnExecute będzie zawsze dostępne do uruchomienia . W destruktorze wystarczy dać wtedy jedynie Terminate i dołożyć wymuszenie
zakończenia gdy TThread::Terminated w klasie obudowującej TThread , coś w tym kierunku ....

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