Strona 1 z 1

Wątek a zmienne prywatne

Nowy postNapisane: czwartek, 19 marca 2009, 13:49
przez GoldWolf
Czy jest jakaś możliwość by wątek miał dostęp do zmiennych prywatnych klasy Forma, albo czy można zrobić z wątku metodę tej klasy.
Po to by między metodami klasy a wątkiem można było przekazywać zmienne(a dokładnie wartości zmiennych).

Kod: Zaznacz cały
namespace watek{
   int p;
}

// Pierwszy wątek dla jednego rdzenia  --------------------------------------
int __fastcall Obciaz1(Pointer Parameter){
int czas = 0, czas1 = 0;
using watek::p;
//czas = Form2->Edit1->Text.ToIntDef(-1);//BŁĄD -> nie działa wartość = -1, a nie zmienne wartości podanej w Edit
czas = p;
//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender){
  using watek::p;
p =  Edit1->Text.ToIntDef(-1);


Zastosowałem przestrzeń, ale wolałbym mieć "p" prywatne, jest jakiś sposób ?

Re: Wątek a zmienne prywatne

Nowy postNapisane: czwartek, 19 marca 2009, 16:19
przez banita
jesli mowa o klasie TThread z bcb to jest to taka sama klasa jak kazda inna. moze ja np zaprzyjaznic z forma i bedziesz mial dostep do zmiennych prywatnych. mozesz w klasie watku wywolywac metody formy i pobrac wartosci. dokladnie tak jak ma to miejscy miedzy dwiema dowolnymi klasami

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 10:39
przez Cyfrowy Baron
Musisz zadeklarować w klasie formularza zmienne publiczne nie prywatne, gdyż do prywatnych dostęp ma tylko klasa w której zostały takie zmienna zadeklarowane.
Gdy zadeklarujesz zmienne w sekcji public to możesz odwoływać się do nich w wątku poprzez odpowienie adresowanie na obiekt formularza do którego przynależą, czyli np. Form1->zmienna = coś tam.

//czas = Form2->Edit1->Text.ToIntDef(-1);//BŁĄD -> nie działa wartość = -1, a nie zmienne wartości podanej w Edit


musi działać, ale zmienna tekst w trakcie działania wątku musi zawierać tekst reprezentujący liczbę całkowitą, by mogła dokonać konwersji, jeżeli tekst nie jest liczbą całkowitą to funkcja zawsze zwróci jako wynik zadeklarowaną wartość. To, że wątek działa nie oznacza, że cały czas sprawdza zawartość obiektu Edit. W Twoim przykładzie sprawdza ją tyko raz po uruchomieniu wątku. Wątek jest taką samą funkcją jak inne tylko uruchomioną w oddzielnym procesie, więc gdy funkcja wykona instrukcje zawarte w ciele funkcji zakończy swoje działanie, co nie jest jednak równoznaczne z zakończeniem wątku, niemniej funkcja nic więcej nie zrobi, gdyż zrobiła już wszystko. Odnoszę wrażenie iż sądzisz, że funkcja działająca jako wątek po zakończeniu działania ponownie się wykonuje, ale to nie jest prawdą, funkcja wykon się tylko raz. Możesz na końcu funkcji umieścić instrukcję powrotu goto, wtedy po dojściu do końca funkcja wróci do początku, np:

Kod: Zaznacz cały
int __fastcall Obciaz1(Pointer Parameter)
{
  Lab_1: // etykieta powrotu

  int czas = 0, czas1 = 0;
  using watek::p;
  czas = Form2->Edit1->Text.ToIntDef(-1);//BŁĄD -> nie działa wartość = -1, a nie zmienne wartości podanej w Edit
  czas = p;

  goto Lab_1; // powrót do etykiety
}


przedstawiony przeze mnie wyżej sposób z funkcja goto jest dopuszczalny tylko w wątku, gdyż kończąc wątek można przerwać nieskończoną pętlę, w przypadku zastosowania w zwykłej funkcji spowoduje to zapętlenie się funkcji, która będzie działała nieprzerwanie.

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 11:08
przez polymorphism
Cyfrowy Baron napisał(a):przedstawiony przeze mnie wyżej sposób z funkcja goto jest dopuszczalny tylko w wątku, gdyż kończąc wątek można przerwać nieskończoną pętlę

Oba rozwiązania są złe. Jeśli chodzi o goto - wiadomo ;) Co do kończenia wątku via TerminateThread (pośrednio lub nie), jest to metoda niszczenia w sytuacjach krytycznych, gdy nie ma możliwości zamknięcia go w sposób typowy, czyli wyskakując z funkcji (thread entry).

Dodatkowo są tam niezsynchronizowane odwołania do kontrolek VCL - to poważny błąd.

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 11:42
przez kinio
Tak, to co pisze polymorphism jest prawdą,
dodatkowo co się tyczy:
polymorphism napisał(a):Dodatkowo są tam niezsynchronizowane odwołania do kontrolek VCL - to poważny błąd.

oczywiście to jest chyba najważniejsze i o tym już kiedyś pisałem:
http://programowanie.cal.pl/forum/viewtopic.php?f=2&t=124&p=738&#p738

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 13:44
przez GoldWolf
Cyfrowy Baron napisał(a):Musisz zadeklarować w klasie formularza zmienne publiczne nie prywatne, gdyż do prywatnych dostęp ma tylko klasa w której zostały takie zmienna zadeklarowane.
Gdy zadeklarujesz zmienne w sekcji public to możesz odwoływać się do nich w wątku poprzez odpowienie adresowanie na obiekt formularza do którego przynależą, czyli np. Form1->zmienna = coś tam.

//czas = Form2->Edit1->Text.ToIntDef(-1);//BŁĄD -> nie działa wartość = -1, a nie zmienne wartości podanej w Edit


To na pewno nie działa, nie wiem co się stało ale się stało. Publiczną zmienną się zadowoliłem.
Wcześniej miałem tak
Kod: Zaznacz cały
int __fastcall Obciaz1(Pointer Parameter){
int czas = 0, czas1 = 0;

     Form2->Edit1->Visible = false;
     Form2->Wyswietl->Caption = "CPU obciążony przez :";
     czas1 = czas = ((GetTickCount()/1000)/60);
     czas += Form2->Edit1->Text.ToInt(); // -> DZIAŁAŁO !!!
     Application->ProcessMessages();

void __fastcall TForm2::Button1Click(TObject *Sender){ 
bool t;
int p;
p = (*(Edit1->Text.c_str( )));
t = isdigit(p) == 0 ? false : true;
if(t == true){

if((GetCPUCount()-1) == 1){
   // inicjalizacja wątku 1
   W_ID = BeginThread(NULL, 0, Obciaz1, this, 0, W_PD);


Ogólnie nie wiem czy znowu się nie stało coś nie przewidzianego ponieważ ten przykład działał dopóki nie zacząłem testować różnych możliwości i tak jak zacząłem wpisywać litery bądź miks cyfr i liter przestało działać. TO znaczy wcześniej działały tylko liczby(nie próbowałem liter). Ponieważ chciałem wyeliminować możliwość wprowadzenia miksów i samych liter dlatego zrobiłem:
Kod: Zaznacz cały
void __fastcall TForm2::Button1Click(TObject *Sender){
using watek::p;
p =  Edit1->Text.ToIntDef(-1);
if(p > 0){
   if((GetCPUCount()-1) == 1){
      // inicjalizacja wątku 1
      W_ID = BeginThread(NULL, 0, Obciaz1, this, 0, W_PD);
   }
   Wyswietl->Caption = "Wybierz czas testu:";
   Edit1->Visible = true;
}else
   ShowMessage("Nie podano liczby");

Jednak po tej czynności przestało mi działać przypisanie z Form2 i musiałem szukać zmiennej.
Też mi się wydawało, że powinno ale nie działało.
Kod: Zaznacz cały
int __fastcall Obciaz2(Pointer Parameter){
using watek::p;
   int czas = 0, czas1 = 0;
   czas1 = czas = ((GetTickCount()/1000)/60);
//   czas += Form2->Edit1->Text.ToInt(); NIE DZIAŁA !!!
// czas +=  Form2->Edit1->Text.ToIntDef(-1); NIE DZIAŁA !!!
   czas += p;


Przez chwile myślałem, że po użyciu tego
Kod: Zaznacz cały
p =  Edit1->Text.ToIntDef(-1);

Edit jest kasowany(to co się w niego wprowadziło). Ogólnie to tak jakby wątek przestał widzieć "czas += Form2->Edit1->Text.ToIntDef(-1);".

A najdziwniejsze się stało teraz(podczas pisania postu) wszystko wróciło do normy i jest OK
czas += Form2->Edit1->Text.ToIntDef(-1); -> już działa.
Widocznie coś przy kompilacji się sknociło i dzisiaj jest już ok.

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 13:52
przez kinio
Hej,

Chyba nie przeczytałeś tego co napisałem - szkoda!
Jeżeli tworzysz wątek, który chce się odwoływać do zmiennych, pól, czy jak to tam sobie nazwiesz które znajdują się w pamięci współdzielonej to program Ci się wywali!
Musisz dokonać synchronizacji!!!

Przeczytaj jeszcze raz co napisałeś:
Dante napisał(a):Widocznie coś przy kompilacji się sknociło i dzisiaj jest już ok.

To nie brzmi jak wytłumaczenie.
Tak, owszem dzisiaj może Ci działać ale jutro, albo nawet zaraz może przestać.

Tworzysz wątek:
Kod: Zaznacz cały
W_ID = BeginThread(NULL, 0, Obciaz1, this, 0, W_PD);

gdzie funckja Obciaz1:
Kod: Zaznacz cały
int __fastcall Obciaz1(Pointer Parameter){
int czas = 0, czas1 = 0;

     Form2->Edit1->Visible = false;
     Form2->Wyswietl->Caption = "CPU obciążony przez :";
     czas1 = czas = ((GetTickCount()/1000)/60);
     czas += Form2->Edit1->Text.ToInt(); // -> DZIAŁAŁO !!!
     Application->ProcessMessages();

masz tutaj odwołania:
- Form2->Edit1->Visible
- Form2->Wyswietl->Caption
- czas += Form2->Edit1->Text.ToInt();

Tak nie może być - nie zauważyłeś że nie działają Ci te fragmenty które odwołują się do pamięci współdzielonej?
Z synchronizuj te odwołania z głównym wątkiem!!

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 13:54
przez Cyfrowy Baron
oczywiście to jest chyba najważniejsze i o tym już kiedyś pisałem:
viewtopic.php?f=2&t=124&p=738&#p738


drobna przeszkoda:


[BCC32 Error] Unit1.cpp(30): E2285 Could not find a match for 'TThread::Synchronize(void)'




Kod: Zaznacz cały
protected procedure TThread.Synchronize(
  Method: TThreadMethod
);


funkcja synchronizowana musi być zadeklarowana w sekcji protected.



Nie wiem o co chodzi z tym Twoim kodem, ale pokaże teraz jak to może wyglądać zgodnie z tym co proponuje kinio w prostym przykładzie, który tylko dodaje wartość pobraną z Edit1 do wartości zmiennej przekazanej do wątku poprzez konstruktor i następnie wyświetla wynik w Edit2. Funkcja Execute zwiera pętlę while(1), dzięki której wątek na bieżąco i nieustannie sprawdza zawartość obiektu Edit1 i dokonuje obliczeń. Funkcja goto sprawdza się tutaj równie dobrze:

Cały kod zawarłem w pliku źródłowym (Unit1.cpp) za wyjątkiem zmiennej p która jest deklarowana w pliku nagłówkowym (Unit1.h) w sekcji private.
Dwa przyciski:
Button1 - wstrzymuje wątek
Button2 - uruchamia wątek
Edit1 - podaje wartość obliczaną w wątku
Edit2 - wyświetla na bieżąco wynik obliczeń dokonywanych w wątku
Kod: Zaznacz cały
//---------------------------------------------------------------------------
class myThread : public TThread
{
    int p;

protected:
    void __fastcall get_p(void);
    void __fastcall Execute(void);

public:

    myThread(int _p):TThread(true), p(_p){}
};
void _fastcall myThread::get_p(void)
{
  int czas = p + Form1->Edit1->Text.ToIntDef(-1);
  Form1->Edit2->Text = czas;
}
void __fastcall myThread::Execute(void)
{
while(1)
{
  Synchronize(get_p);
}
/*LAB_1:
  Synchronize(get_p);
goto LAB_1; */
}

  myThread* thread;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
  p = 100;
  thread = new myThread(Form1->p);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  thread->Suspend();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
  thread->Resume();
}
//---------------------------------------------------------------------------


Inny sposób to przekazanie do wątku wskaźnika na formularz np. Form1, dzięki czemu będziesz miał dostęp do wszystkiego co znajduje się na tym formularzu, z tym, że wątek ma w tej sytuacji dostęp tylko do obiektów, funkcji, zmiennych itp zadeklarowanych w pliku nagłówkowym (np. Unit1.h) w sekcjach __published i public, tak więc zmienną p należy w tym przypadku zadeklarować w sekcji public, a nie jak wyżej w private.
Można zastosować dwie metody.
Pierwsza zgodna z tym co przedstawił kinio:


Kod: Zaznacz cały
class myThread : public TThread
{
    TForm1 *tForm;

protected:
    void __fastcall get_p(void);
    void __fastcall Execute(void);

public:
   myThread(TForm1 *_form):TThread(true), tForm(_form){}
};
//---
void _fastcall myThread::get_p(void)
{
  int czas = tForm->p + tForm->Edit1->Text.ToIntDef(-1);
  tForm->Edit2->Text = czas;
}
void __fastcall myThread::Execute(void)
{
while(1)
{
  Synchronize(get_p);
}
/*LAB_1:
  Synchronize(get_p);
goto LAB_1; */
}

myThread* thread;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
p = 100;
thread = new myThread(Form1);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
thread->Suspend();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
thread->Resume();
}
//---------------------------------------------------------------------------



i druga z definicją konstruktora klasy:

Kod: Zaznacz cały
class myThread : public TThread
{
    TForm1 *tForm;

protected:
    void __fastcall get_p(void);
    void __fastcall Execute(void);

public:

    __fastcall myThread(TForm1 *Form);
};
__fastcall myThread::myThread(TForm1 *Form)
  : TThread(Form)
{
tForm = Form;
}
//---
void _fastcall myThread::get_p(void)
{
  int czas = tForm->p + tForm->Edit1->Text.ToIntDef(-1);
  tForm->Edit2->Text = czas;
}
void __fastcall myThread::Execute(void)
{
while(1)
{
  Synchronize(get_p);
}
/*LAB_1:
  Synchronize(get_p);
goto LAB_1; */
}

myThread* thread;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
p = 100;
thread = new myThread(Form1);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
thread->Suspend();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
thread->Resume();
}
//---------------------------------------------------------------------------


W przykładach podaję jako alternatywę funkcję goto, gdyż ma tutaj zastosowanie.

zaczerpnięte z Wikipedii

goto to nazwa instrukcji w językach programowania, która powoduje bezwarunkowe przekazanie sterowania w inne miejsce (tzw. skok). Występuje w tak odległych od siebie językach jak Fortran, Algol, COBOL, SNOBOL, Basic, C/C++, Perl, Pascal, nadchodzącym PHP6 i wielu innych. Instrukcja goto może zaciemniać strukturę programu (zwłaszcza, gdy skok odbywa się między dwoma nie związanymi ze sobą blokami kodu, np. z ciała jednej klasy do drugiej), dlatego zaleca się unikać jej stosowania, zastępując goto innymi konstrukcjami programowymi.

Instrukcja Goto często odnosi się do etykiety lub numeru linii w źródle programu.




Funkcji goto nie należy raczej stosować tam, gdzie można użyć innej metody, np. pętli while, jednak czasami może się okazać, że nie można postąpić inaczej i wtedy funkcja goto się przydaje. Nie natknąłem się jednak na taką sytuację, jestem jednak mocno przyzwyczajony do tej funkcji, dlatego często jej używam.

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 14:16
przez kinio
Cyfrowy Baron napisał(a):drobna przeszkoda:
[BCC32 Error] Unit1.cpp(30): E2285 Could not find a match for 'TThread::Synchronize(void)'

Nowe środowiska wymagają aby podać jeszcze & przed nazwą funkcji!!
Czyli zamiast:
Kod: Zaznacz cały
Synchronize(funckja);

piszemy:
Kod: Zaznacz cały
Synchronize(&funckja);


Poza tym z tego co pamiętam, to nie trzeba deklarować ani definiować tej funkcji.
Ona już jest w klasie TThread!

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 17:16
przez Cyfrowy Baron
piszemy:
Kod: Zaznacz cały
  Synchronize(&funckja);



To nie działa. Prawdą jest, że funkcji Synchronize się nie deklaruje ani nie definiuje, jednak funkcję którą się przekazuje jako argument funkcji Synchronize należy umieścić w sekcji protected, tak jak to podałem w przykładach.

funkcja synchronizowana musi być zadeklarowana w sekcji protected.


nie chodziło mi o funkcję Synchronize, tylko o funkcję poddawaną synchronizacji czyli w przytoczonym przez Ciebie przkładzie deklarujemy w sekcji protected funkcję funkcja:

Kod: Zaznacz cały
protected:
        void __fastcall funkcja(void);

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 18:28
przez wargo
Cyfrowy Baron napisał(a):
funkcja synchronizowana musi być zadeklarowana w sekcji protected.


nie chodziło mi o funkcję Synchronize, tylko o funkcję poddawaną synchronizacji czyli w przytoczonym przez Ciebie przkładzie deklarujemy w sekcji protected funkcję funkcja:

Przecież o tym pisał!

Re: Wątek a zmienne prywatne

Nowy postNapisane: piątek, 20 marca 2009, 18:39
przez GoldWolf
Teraz to już sam się pogubiłem trochę.
Dobra to zacznijmy to porządkować, jeżeli używa się wątków może się tak zdarzyć, iż wątek z powodu braku synchronizacji będzie miał problemy z odczytaniem zmiennych przekazywanych bezpośrednio z obiektów(mechanizmów Formy).

Ja raczej zwaliłbym problem z odczytem na to, iż przy testach kompilator coś pochrzanił, a raczej ja próbując testować.
Postaram się w najbliższym dniach przyjrzeć waszym rozwiązaniom, ponieważ na razie jest to dla mnie czarna magia i na spokojnie muszę to przetestować.

kinio napisał(a):Hej,

Chyba nie przeczytałeś tego co napisałem - szkoda!
Jeżeli tworzysz wątek, który chce się odwoływać do zmiennych, pól, czy jak to tam sobie nazwiesz które znajdują się w pamięci współdzielonej to program Ci się wywali!
Musisz dokonać synchronizacji!!!

Przeczytaj jeszcze raz co napisałeś:
Dante napisał(a):Widocznie coś przy kompilacji się sknociło i dzisiaj jest już ok.
To nie brzmi jak wytłumaczenie.
Tak, owszem dzisiaj może Ci działać ale jutro, albo nawet zaraz może przestać.

Nie mogę się z tobą zgodzić ponieważ aplikacja działa(cały czas jej używam tylko chciałem ją ulepszyć), błąd owszem się pojawił "p is not intiger"(czy jakoś tak), gdyż nie miałem zabezpieczenia przed wpisywaniem liter(znaków innych niż liczby), akurat to moja wina. Nie mówię, że synchronizacja nie rozwiązuje problemu. Ostatnio mi zniknęła forma więc winą obarczyłem i w tym wypadku kompilator.
Nie chcesz mi mówić, iż jak mam zmienną p = 3 - to ta zmienna się zmienia i nie ważne czy to jest wątek czy coś innego sam podałem przykład, który rozwiązuje problem stosując przestrzeń nazw.

Jak pisał Baron
Cyfrowy Baron napisał(a):Gdy zadeklarujesz zmienne w sekcji public to możesz odwoływać się do nich w wątku poprzez odpowienie adresowanie na obiekt formularza do którego przynależą, czyli np. Form1->zmienna = coś tam.

//czas = Form2->Edit1->Text.ToIntDef(-1);//BŁĄD -> nie działa wartość = -1, a nie zmienne wartości podanej w Edit


[j]musi działać

I działa, czemu nie działało chciałbym sam wiedzieć, przeanalizuje to zobaczę. Gdybym się przespał z problemem to być może tematu w ogóle by nie było, nadal stawiam na kompilator, bądź błędy spowodowane przeze mnie przy testowaniu....