C++/CLI, szálkezelés

C++/CLI, szálkezelés
2011-11-07T22:32:19+01:00
2011-11-09T22:07:51+01:00
2022-11-23T15:41:55+01:00
Delphi_Fan
Sziasztok!

Van egy függvényem (frmCopyRenMov.cpp-ben):
System::Void frmCopyRenMov::ReadContent(){ if(this->mainForm->StopThis == true) return; array<String^>^ tartalom = gcnew array<String^>(0); int RecursiveOn = 1; String^ SourceDir = ""; this->mainForm->storingfileCount = 0; ListView^ lv = this->mainForm->GetActiveLV(); ListViewItem^ lvElem = nullptr; for(int idx=0; idx<lv->Items->Count; idx++){ if(this->mainForm->StopThis == true){ break; } lvElem = lv->Items[idx]; if(lvElem->ForeColor == System::Drawing::Color::Red){ Tartalom(this->mainForm->AddBackSlash(lv->Parent->Text) + lv->Items[idx], 1); } } // MessageBox::Show(this->mainForm->lista->Length.ToString()); }
A this->mainForm az alkalmazás legelső form-jára mutat, neve: Form1.
Ezt akarom meghívni egy szálból (frmCopyRenMov.h-ban):

System::Void ReadContent(); //31.sor ... ref class GettingListThread{ public: static void Do_Work(){ void ReadContent(); } }; ... //Így hívom meg: // GettingListThread^ glt = gcnew GettingListThread(); // ThreadStart^ ts = gcnew ThreadStart(this, &frmCopyRenMov::ReadContent); ThreadStart^ ts = gcnew ThreadStart(&GettingListThread::Do_Work); //működik, viszont csak elméletben. Nem hajtódik végre a Do_Work tartalma :( ts->Invoke(); Thread^ t = gcnew Thread(ts); t->Start(); if(t->ThreadState == System::Threading::ThreadState::Running){ MessageBox::Show("Thread is running!"); // működik, viszont csak az üzi dialógust nyomja. } t->Abort();

Amennyiben ezzel próbálkozom:
ref class GettingListThread{ public: static void Do_Work(){ void ReadContent(); } };
Ezt kapom:
1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2352: 'Aktamenedzser::frmCopyRenMov::ReadContent' : illegal call of non-static member function
1> c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(31) : see declaration of 'Aktamenedzser::frmCopyRenMov::ReadContent'
1> frmCopyRenMov.cpp
1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2352: 'Aktamenedzser::frmCopyRenMov::ReadContent' : illegal call of non-static member function
1> c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(31) : see declaration of 'Aktamenedzser::frmCopyRenMov::ReadContent'
1> Generating Code...


Amennyiben megadom neki amit akar (
System::Void ReadContent(); => static System::Void ReadContent();
), ez lesz:
1> Akta menedzser.cpp
1> frmCopyRenMov.cpp
1>frmCopyRenMov.cpp(58): error C2355: 'this' : can only be referenced inside non-static member functions
1>frmCopyRenMov.cpp(58): error C2227: left of '->mainForm' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(58): error C2227: left of '->StopThis' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(62): error C2355: 'this' : can only be referenced inside non-static member functions
1>frmCopyRenMov.cpp(62): error C2227: left of '->mainForm' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(62): error C2227: left of '->storingfileCount' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(63): error C2355: 'this' : can only be referenced inside non-static member functions
1>frmCopyRenMov.cpp(63): error C2227: left of '->mainForm' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(63): error C2227: left of '->GetActiveLV' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(66): error C2355: 'this' : can only be referenced inside non-static member functions
1>frmCopyRenMov.cpp(66): error C2227: left of '->mainForm' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(66): error C2227: left of '->StopThis' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(71): error C2355: 'this' : can only be referenced inside non-static member functions
1>frmCopyRenMov.cpp(71): error C2227: left of '->mainForm' must point to class/struct/union/generic type
1>frmCopyRenMov.cpp(71): error C2227: left of '->AddBackSlash' must point to class/struct/union/generic type


Végezetül, amennyiben:
//Ezt a sort: ThreadStart^ ts = gcnew ThreadStart(&GettingListThread::Do_Work); //Erre változtatom: ThreadStart^ ts = gcnew ThreadStart(this, &frmCopyRenMov::ReadContent);
ezt kapom:
An unhandled exception of type "System.InvalidOperationException" occured in System.Windows.Forms.dll

Additional information: Cross-thread operation not valid: Control "lvLeft" accessed from a thread other than the thread it was created on.


Mi a megoldás?

Üdv.: Delphi_Fan

ui.: Goolge barátommal igen sokat beszélgettünk a dolgokról.
Mutasd a teljes hozzászólást!
Gondolom ugyanaz a szál hozza létre a frmCopyRenMov formot is és a mainForm-ot. Ebben az esetben egyszerűen frmCopyRenMov::ReadContent-be kell az invoke required, valahogy így:
//ez a sor frmCopyRenMov header-be kellene az frmCopyRenMov osztályhoz. delegate System::Void ReadContentDelegate(); System::Void frmCopyRenMov::ReadContent(){ if(!this->InvokeRequired) { // itt az eredeti függvénykódot ird be amit Te irtal } else { ReadContentDelegate aDelegate = gcnew ReadContentDelegate( this, &frmCopyRenMov::ReadContent ); this->Invoke(aDelegate); } }

Ezt persze ismét nem tudtam kipróbálni VS2010 alatt, de vázlatnak jó.

A szál indítása amennyiben az frmCopyRenMov valamely metódusában van, akkor így lehet elindítani a ReadContent-et külön szálon:

ThreadStart^ ts = gcnew ThreadStart(this, &frmCopyRenMov::ReadContent); Thread^ t = gcnew Thread(ts); t->Start();

Amennyiben valahol máshol, akkor kell egy példány az frmCopyRenMov-ból, és azt kell átadni a ThreadStart-nak a this helyett. A GettingListThread osztály szerintem felesleges ide.

Van még egy hátulütője, ha oda teszed az InvokeRequiredet ahova mondtam: ugyan működni fog, de valójában így sem lesz igazán jól megoldva a dolog, mert így amit egy szálon akartál végrehajtani (a teljes ReadContent), az tulajdonképpen a form száljában kerül majd végrehajtásra, és szerintem ugyanúgy blokkolni fogja a formodat. Én valójában különválasztanám a költséges műveleteket (gondolom ezeket akarod háttérben futtatni) a formok hívásaitól, és az InvokeRequired-et bele tenném a form kódjába.

A vázlat valami ilyesmi a GetActiveLV()-re:
delegate ListView^ GetActiveLVDelegate(); ListView^ Form1::GetActiveLV(){ if(!this->InvokeRequired) { // itt az eredeti GetActiveLV függvénykódot } else { GetActiveLVDelegate aDelegate = gcnew GetActiveLVDelegate( this, &Form1::GetActiveLV ); this->Invoke(aDelegate); } }

Ezután a ReadContent-ben nem kell InvokeRequired ha megcsinálod mindezt hasonlóan a mainform összes olyan dolgaira, amihez a szálból nyúlsz:

System::Void frmCopyRenMov::ReadContent(){ if(this->mainForm->GetStopThis() == true) return; array<String^>^ tartalom = gcnew array<String^>(0); int RecursiveOn = 1; String^ SourceDir = ""; this->mainForm->SetStoringfileCount(0); ListView^ lv = this->mainForm->GetActiveLV(); ListViewItem^ lvElem = nullptr; for(int idx=0; idx<lv->Items->Count; idx++){ if(this->mainForm->GetStopThis() == true){ break; } if(this->mainForm->GetLVElemForeColor(lv, idx) == System::Drawing::Color::Red) { Tartalom(this->mainForm->AddBackSlash(lv) + this->mainForm->GetLVElem(lv,idx), 1); } } }

Amit itt írtam, nyilvánvalóan nem fog elsőre működni, de talán kiindulásnak jó. A szálak kezelése a programozásnak nem egyszerű részei, és itt még a GUI is bekavar.

Amit írtál a programfejlesztési munkáról, én kicsit másképp látom. Én keresgélés nélkül nem tudnék dolgozni, mert sokszor olyan problémákat kell megoldani, amit az ember nem tud fejből, hiszen nem találkozhat mindennel. A nyelvvel persze tisztában kell lenni, de még soha nem fordult elő, hogy valamelyik főnököm azért piszkált volna, hogy bújom a google-t, és nem tudom fejből megcsinálni a dolgokat.
Mutasd a teljes hozzászólást!

  • Bevallom őszintén, nem olvastam végig teljesen amit írtál, de az általános visszatérő probléma, amikor winforms-ot másik szálból hívunk, akkor jön a " Cross-thread operation not valid" exception.

    Ennek kiküszöbölésére az invoke és az invokerequired hasznos. Itt már másnak írtam róla, igaz az C# volt, itt is szerintem ez lesz a megoldás:
    link

    Ha nem, akkor bocs.
    Mutasd a teljes hozzászólást!
  • Akkor talán el kéne olvasni :(
    Ott kezdődik a probléma, hogyha C++/CLI-ben kérek segítséget, akkor C++/CLI-ben várom a választ.

    Továbbá, írtam egy ilyet:

    ref class GettingListThread{ public: [b]static[/b] void Do_Work(){ void ReadContent(); } };
    Erre:
    if(InvokeRequired){ }
    értelemszerűen ezt kapom:

    1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2327: 'System::Windows::Forms::Control::InvokeRequired' : is not a type name, static, or enumerator

    1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2327: 'System::Windows::Forms::Control::InvokeRequired' : is not a type name, static, or enumerator

    1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2065: 'InvokeRequired' : undeclared identifier

    Erre:
    if(this->InvokeRequired){ }
    meg ezt:
    1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2355: 'this' : can only be referenced inside non-static member functions
    1>c:\vs 2010 projects\akta menedzser\frmCopyRenMov.h(51): error C2227: left of '->InvokeRequired' must point to class/struct/union/generic type


    Én a helyedben legalább elindítottam volna az MSVS 2010-et, és megnéztem volna.

    Ezért akarom megtanulni a C++-t. Jó munkát!
    Mutasd a teljes hozzászólást!
  • Ott kezdődik a probléma, hogyha C++/CLI-ben kérek segítséget, akkor C++/CLI-ben várom a választ.


    Ha C++-ban akarod a .Net-et programozni, előbb-utóbb rá kell jöjj, hogy C#-ban írt dolgok is nagyon hasznosak tudnak lenni. Nem annyira nehéz C#-os dolgokat átírni C++-ra.

    Akkor talán el kéne olvasni :(


    Kéne de a mai világban nincs mindenre időm, és a lényeget kihámoztam, és szerintem rájöttem, mivel van a bajod, és segítettem is. Neked meg nem kéne ilyen fennhangon követelődzve írni, ha valaki segíteni próbál valamit. Írhattad volna szebben is. Pl:
    "oké, köszi a megoldást, kipróbáltam az invokerequired-et, de ez és ez a hiba jött, stb ."
    Az állás megpályázásához kitartást kívánok, de helyedben a stílusodon is változtatnék, ha programozni akarsz. A követelődzés és kioktatás a mindent jobban tudó menedzsereknél divat.

    Én a helyedben legalább elindítottam volna az MSVS 2010-et, és megnéztem volna.


    Otthon nincs VS2010-em, debianom van, igy erre esélyem sem volt, de lásd feljebb, nem is kellett volna, mert rájöttem, mi a problémád lényege. A formod Thread1-ben jött létre, te pedig Thread2-ből akarsz belehívni. Ez így nem működik .Net-ben winforms alatt (egyébként GTK#-ban sem).

    Szerintem rossz helyre tetted az InvokeRequired-et, abból adódott a fordítási hibád. És bizony tudnék még segíteni, de mivel csak C++/CLI-ben várod a választ, ezért nem írok többet. Ráadásul benn a melóhelyen is csak VS2008-van, szóval így VS2010-ben sem tudom neked a megoldást prezentálni.
    Mutasd a teljes hozzászólást!
  • Ha C++-ban akarod a .Net-et programozni, előbb-utóbb rá kell jöjj, hogy C#-ban írt dolgok is nagyon hasznosak tudnak lenni.

    Mindegy milyen nyelv.. a funkció a lényeg.

    Nem annyira nehéz C#-os dolgokat átírni C++-ra.

    Annak, aki perfektül ismeri mindkét nyelvet.

    Kéne de a mai világban nincs mindenre időm...

    Megértem, hogy kevés az időd, viszont segíteni csak úgy tudsz, ha tökéletesen érted az egyén problémáját.

    Az állás megpályázásához kitartást kívánok...

    Sajnos már nem lesz megpályázva, mert:
    1) Nincs értelme elmenni semmilyen céghez megfelelő tudás nélkül.
    2) Amikor dolgozni megyek, még akkor sem kezdhetek el keresgélni semmit, még akkor sem, ha programfejlesztés a munkám. :(

    Szerintem rossz helyre tetted az InvokeRequired...

    Hova tegyem??
    Mutasd a teljes hozzászólást!
  • Gondolom ugyanaz a szál hozza létre a frmCopyRenMov formot is és a mainForm-ot. Ebben az esetben egyszerűen frmCopyRenMov::ReadContent-be kell az invoke required, valahogy így:
    //ez a sor frmCopyRenMov header-be kellene az frmCopyRenMov osztályhoz. delegate System::Void ReadContentDelegate(); System::Void frmCopyRenMov::ReadContent(){ if(!this->InvokeRequired) { // itt az eredeti függvénykódot ird be amit Te irtal } else { ReadContentDelegate aDelegate = gcnew ReadContentDelegate( this, &frmCopyRenMov::ReadContent ); this->Invoke(aDelegate); } }

    Ezt persze ismét nem tudtam kipróbálni VS2010 alatt, de vázlatnak jó.

    A szál indítása amennyiben az frmCopyRenMov valamely metódusában van, akkor így lehet elindítani a ReadContent-et külön szálon:

    ThreadStart^ ts = gcnew ThreadStart(this, &frmCopyRenMov::ReadContent); Thread^ t = gcnew Thread(ts); t->Start();

    Amennyiben valahol máshol, akkor kell egy példány az frmCopyRenMov-ból, és azt kell átadni a ThreadStart-nak a this helyett. A GettingListThread osztály szerintem felesleges ide.

    Van még egy hátulütője, ha oda teszed az InvokeRequiredet ahova mondtam: ugyan működni fog, de valójában így sem lesz igazán jól megoldva a dolog, mert így amit egy szálon akartál végrehajtani (a teljes ReadContent), az tulajdonképpen a form száljában kerül majd végrehajtásra, és szerintem ugyanúgy blokkolni fogja a formodat. Én valójában különválasztanám a költséges műveleteket (gondolom ezeket akarod háttérben futtatni) a formok hívásaitól, és az InvokeRequired-et bele tenném a form kódjába.

    A vázlat valami ilyesmi a GetActiveLV()-re:
    delegate ListView^ GetActiveLVDelegate(); ListView^ Form1::GetActiveLV(){ if(!this->InvokeRequired) { // itt az eredeti GetActiveLV függvénykódot } else { GetActiveLVDelegate aDelegate = gcnew GetActiveLVDelegate( this, &Form1::GetActiveLV ); this->Invoke(aDelegate); } }

    Ezután a ReadContent-ben nem kell InvokeRequired ha megcsinálod mindezt hasonlóan a mainform összes olyan dolgaira, amihez a szálból nyúlsz:

    System::Void frmCopyRenMov::ReadContent(){ if(this->mainForm->GetStopThis() == true) return; array<String^>^ tartalom = gcnew array<String^>(0); int RecursiveOn = 1; String^ SourceDir = ""; this->mainForm->SetStoringfileCount(0); ListView^ lv = this->mainForm->GetActiveLV(); ListViewItem^ lvElem = nullptr; for(int idx=0; idx<lv->Items->Count; idx++){ if(this->mainForm->GetStopThis() == true){ break; } if(this->mainForm->GetLVElemForeColor(lv, idx) == System::Drawing::Color::Red) { Tartalom(this->mainForm->AddBackSlash(lv) + this->mainForm->GetLVElem(lv,idx), 1); } } }

    Amit itt írtam, nyilvánvalóan nem fog elsőre működni, de talán kiindulásnak jó. A szálak kezelése a programozásnak nem egyszerű részei, és itt még a GUI is bekavar.

    Amit írtál a programfejlesztési munkáról, én kicsit másképp látom. Én keresgélés nélkül nem tudnék dolgozni, mert sokszor olyan problémákat kell megoldani, amit az ember nem tud fejből, hiszen nem találkozhat mindennel. A nyelvvel persze tisztában kell lenni, de még soha nem fordult elő, hogy valamelyik főnököm azért piszkált volna, hogy bújom a google-t, és nem tudom fejből megcsinálni a dolgokat.
    Mutasd a teljes hozzászólást!
  • Köszi szépen!

    Végre sikerült megcsinálnom :)

    Üdv.: Delphi_Fan
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd