Várakozás, polling (Delphi)
2015-02-05T10:58:24+01:00
2015-05-27T09:17:11+02:00
2022-07-22T08:42:53+02:00
  • Köszönöm válaszod, de azóta már változott a helyzet, ha megnézed a hozzászólásokat. 
    Mutasd a teljes hozzászólást!
  • De igazából erre is van több megoldás, például logikai változót állítok be válasz érkezéskor (OnRx esemény), majd figyelem a Timer-ben (ami lenne maga a "főciklus", amit nekem kb. 5-10 másodpercenként kell végrehajtani), hogy jött-e már válasz, illetve egy másik Timerrel pedig a TimeOut-ot állítom be.

    Ennek nem tűnik úgy, hogy temérdek értelme van..
    Miért nem azt csinálod, hogy szinkronizálva beállítasz egy vcl threadben lévő specifikus eseményvezérlőt az OnRx-nek?
    Mutasd a teljes hozzászólást!
  • Időközben megbarátkoztam a megoldással, és most próbálom így ki. 
    Köszönöm! 
    Mutasd a teljes hozzászólást!
  • Nos, először is, nagyon szépen köszönöm, hogy ilyen kitartóan segítettél. Azonban most már nagyon elvesztem, úgyhogy szerintem folytatom ott, ahol elkezdtem (illetve ahol megörültem hogy a jóúton járok), mert sajnos kevés időm van, és hetek óta nem jutok 1-ről a 2-re. Ha most megint újra kezdem, sose lesz vége.
    Ez most úgy hangzik, mint ha türelmetlen volnék, ami lehet részben igaz is.
    Szóval ha nem okoz túl nagy gubancot az op.rendszerben, akkor a unitok közé berakom a másikat, pl. a unit1-hez a unit2-t, és fordítva. Továbbá megpróbálok minél kevesebb (gyakorlatilag közelíteni a 0-hoz) publikus, ill. globális változót használni. De valahol muszáj megállapodnom, mert sose leszek kész.
    Köszönöm mégegyszer!
    Mutasd a teljes hozzászólást!
  • FThreadHasWork-öt sem érem el akkor így a főszálból, pedig az lenne a lényege.

    Nem is kell. Nézd csak ezt:

    unit unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private MyInstance: MyClass; public { Public declarations } end; var Form1: TForm1; // <---------------- itten van a globális változó { Don't declare more globals pls! } implementation procedure TForm1.FormCreate(Sender: TObject); begin MyInstance := MyClass.Create; end; procedure TfrmMain.FormDestroy(Sender: TObject); begin MyInstance.Free; end; procedure Button1Click(Sender: TObject); begin MyInstance.StartThread; // <----------- Elindítod a thread-et end; procedure Button2Click(Sender: TObject); begin MyInstance.StopThread; // <----------- Leállítod a thread-et end;
    Mutasd a teljes hozzászólást!
  • Uhh, na most megint ott vagyok, ahol a part szakad.
    Teljesen tanácstalan lettem. Akkor így nem érem el a főszál komponenseit, meg semmit sem, vagyis gondolom igen, csak újra kell írni az egész mindent, sokkal bonyolultabban, és így is csak most ismerkedek a szálkezeléssel. Persze nem akarok kókány munkát kiadni a kezeim közül, csak most úgy érzem magam, mint aki az erdőbe kóvályog össze-vissza, és nem találja a kiutat.


    Szerk.: a FThreadHasWork-öt sem érem el akkor így a főszálból, pedig az lenne a lényege.
    Mutasd a teljes hozzászólást!
  • Viszont akkor nem érem el a mellék szálból, csak akkor, ha unit2-ben deklarálom, a public részben.


    Még az sem az igazi. Tekintve, hogy a FThreadHasWork-öt a TThread leszármazottad használja, magában a TThread leszármazott-ban érdemes deklarálni kb. így:
    implementation uses { ... }; type MyClass = class(TThread) private FThreadHasWork: TEvent; FParameter: ParameterType; protected procedure Execute(); public constructor Create(const Parameter: ParameterType); destructor Destroy; override; procedure StopThread; procedure StartThread; end; interface constructor MyClass.Create(const Parameter: ParameterType); begin inherited Create; FThreadHasWork := TEvent.Set(); end; destructor MyClass.Destroy(); begin Terminate; StartThread; WaitFor; FThreadHasWork.Free(); inherited Destroy(); end; procedure MyClass.Execute(); begin { ...i won't write it again... } end; procedure MyClass.StopThread; begin FThreadHasWork.UnSet; end; procedure MyClass.StartThread; begin FThreadHasWork.Set; end;

    Érdekes, hogy ha unit1-ben (vagyis a főszál) deklarálom, akkor sehogy se tudom elérni a mellékszálban (unit2-ben).


    A unit2-ből azért nem látod a unit1-ben definiált változót, mert a unit2 "uses" listájában (nagyon helyesen) nem szerepel a unit1. Amúgy a helyes gyakorlat, ha soha nem használsz globális változót. Ez is olyan mint a goto lehet élni nélküle, ha rendesen megtervezed a programot, de ha használod attól csak követhetetlen lesz a logika.

    Globális változó az, amit nem egy függvény vagy metódus fejlécében deklarálsz. Jól megírt Delphi/VCL programokban az egyetlen globális változó a főform ami a form unitjában deklarálódik valahogy így:

    interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; // <---------------- itten van a globális változó { Don't declare more globals pls! }

    Amúgy, ha esetleg félreértettelek és nem globálisan deklaráltad az FThreadHasWork-öt hanem a Formod public mezőjeként, akkor a Formod függvényein kívülről (a unit1-et uses listájában felsoroló unitokból) Form1.FThreadHasWork-ként hivatkozhatnál rá, de azt ne tedd!
    Mutasd a teljes hozzászólást!
  • A te esetedben a FThreadHasWork pl. tipikusan private mezőnek való,

    Viszont akkor nem érem el a mellék szálból, csak akkor, ha unit2-ben deklarálom, a public részben. Érdekes, hogy ha unit1-ben (vagyis a főszál)  deklarálom, akkor sehogy se tudom elérni a mellékszálban (unit2-ben).
    Mutasd a teljes hozzászólást!
  • Válasz:
    A destruktort hagyományosan a public szekcióban deklaráljuk, de gyakorlatilag semmi következménye nem lesz, ha a protectedbe teszed.

    Kifejtés:
    A private, protected és public között az a külünbség, hogy...

    A private, szekcióban deklarált mezők, metódusok és property-k kizárólag az osztályod számára láthatóak. Tipikusan ide szoktak kerülni (hagyományos paradigma szerint) a *belső állapotváltozók (mezők), amiket **kívülről nem akarsz elérni, vagy (az újabb paradigma szerint) az osztályod összes mezője, amiket azután public propertyken keresztül tudsz elérni **"kívül"-ről.

    A protected, szekcióban deklarált mezők, metódusok és property-k kizárólag az osztályod és annak leszármazottai számára láthatóak. Tipikusan ide szoktak kerülni azok a függvények amik *"belső"-nek számítanak, de az osztályod leszármazottai is direkt módon hívják(hívhatják) őket. (**"Kívülről" viszont nem elérhetőek.) Jól szervezett kódokban nagyon ritkán indokolt protected mezőket (változókat), és property-ket deklarálni.

    A public, szekcióban deklarált mezők, metódusok és property-k **kívülről is láthatóak. Tipikusan ide szoktak kerülni azok a metódusok (függvények) amik az osztályod belső változóin végeznek olyan műveleteket amiket a hívó fél **kívülről kezdeményez (pl. a TStringList.Delete()), a property-k amikkel a belső változók tartalmát tudod (legtöbbször) kiolvasni vagy (egyes esetekben) módosítani. Jól szervezett kódokban nagyon ritkán indokolt public mezőket (változókat) deklarálni.

    Magyarázat:
    A fenti szövegben kicsit "konyhanyelven" fogalmaztam.

    *belül(ről) alatt azt értettem, mikor az osztályod deklarált metódusainak törzséből érsz el valamit. pl.:

    constructor MyClass.Create(); begin // innen eléred a MyClass private mezőit (változóit) és függvényeit, és a MyClass összes ősének protected mezőit (változóit) és függvényeit, de az ősök private mezőit (változóit) és függvényeit nem. end; destructor MyClass.Destroy(); begin // innen eléred a MyClass private mezőit (változóit) és függvényeit, és a MyClass összes ősének protected mezőit (változóit) és függvényeit, de az ősök private mezőit (változóit) és függvényeit nem. end; procedure MyClass.MyMethod(); begin // innen eléred a MyClass private mezőit (változóit) és függvényeit, és a MyClass összes ősének protected mezőit (változóit) és függvényeit, de az ősök private mezőit (változóit) és függvényeit nem. end;

    **kívül(ről) alatt azt értettem, mikor az osztályod példányain végezett műveletek során érsz el valamit. pl.:
    procedure MyForm.btnDoSomethingClick(Sender: TObject); var MyInstance: MyClass; intMyValue: integer; begin { create MyClass instance } MyInstance := MyClass.Create(); try MyInstance.MyProperty := "MyValue"; // <----- innen MyInstance.MyMethod(); // <----- innen intMyValue := MyInstance.MyMember; // <----- innen finally { release MyClass instance ... since finally will run anyway, MyInstance won't leak } MyInstance.Free(); end; end;

    A jó kódot többek között az különbözteti meg a silánytól, hogy nem "fedi fel" olyan mezőit (változóit) és függvényeit a világ számára, amikhez senkinek semmi köze. A te esetedben a FThreadHasWork pl. tipikusan private mezőnek való, hiszen a "világnak" semmi köze hozzá, az osztályod "belül" kezeli.
    Mutasd a teljes hozzászólást!
  • Köszönöm! Szuperül működik, még annyi, hogy az execute a "protected" részben van deklarálva, akkor ide jön a destruktor rész is, szóval gondolom nem baj, hogy nem a public részben van (a unit2-ben).

    Szerk.: más: a .Resume helyett ajánlatosabb a .Start-ot használni? Ezt most csak úgy általánosságában kérdezem..
    Mutasd a teljes hozzászólást!
  • Köszi, de nem vagyok sajnos olyan szinten, hogy ilyet tudjak csinálni. 
    Mutasd a teljes hozzászólást!
  • Ha már egyszer busy waitingelni kell, akkor tényleg nagyon csúnya a sleep.
    Én cryptocurrencyt szoktam bányásztatni a ciklusban. Nagyon jó, mert mert viszonylag determinisztikus ideig fut és közben valami hasznosat csinál a gép (a sleeppel ellentétben).
    Mutasd a teljes hozzászólást!
  • Ahol a .Execute()metódusát is.
    Mutasd a teljes hozzászólást!
  • A mellékszál destruktorát hol illik deklarálni? (Most gyakorlatilag van egy unit1 a főszálnak és egy unit2 a mellékszálnak).
    Mutasd a teljes hozzászólást!
  • ha terminálom és felszabadítom a mellékszálat, amiben benne van a TEvent objektum, akkor az is felszabadul?


    Ha felszabadítod a TThread-leszármazottadat (.Free()-t hívsz rá), akkor le fog futni a destruktora. Ha a destruktorában(a YourTThreadDescendent.Destroy()-ban) szerepel az FThreadHasWork.Free(); sor, akkor természetesen az FThreadHasWork TEvent is fel fog szabadulni.
    Mutasd a teljes hozzászólást!
  • 2) A destruktor lefutása után az objektum által lefoglalt összes memória (pl. az objektum mezői) "kifut a scope-ból", azaz a nem "kézzel" lefoglalt dinamikus változók kivételével felszabadul. Ebből a szempontból a virtuális metódusok is mezőnek számítanak.

    Szerintem ez válasz lenne a következő kérdésemre, azaz: ha terminálom és felszabadítom a mellékszálat, amiben benne van a TEvent objektum, akkor az is felszabadul? Bár gondolom nem ilyen egyszerű.. De ha igen, akkor nagyon boldog leszek. 
    Mutasd a teljes hozzászólást!
  • Viszont visszakanyarodva az előző problémához, lényeg az, hogy csak akkor szabadíthatom fel a másik szálban létrehozott objektumot, ha lefutott az execute?

    Igen, kb. erről lenne szó.

    Részletesen leírtad a dolgokat, csak tényleg át akarom látni az egészet, azért kérdezősködök ennyit..

    Semmi gond. Sosem tartottam zavarónak, ha valaki meg akart tanulni/érteni valamit.
    Mutasd a teljes hozzászólást!
  • Na közben megvilágosodtam, köszi! :)
    A WaitFor az ott is van, ahogy írtad, csak most nem írtam ide. 
    Viszont visszakanyarodva az előző problémához, lényeg az, hogy csak akkor szabadíthatom fel a másik szálban létrehozott objektumot, ha lefutott az execute? Részletesen leírtad a dolgokat, csak tényleg át akarom látni az egészet, azért kérdezősködök ennyit.. 
    Mutasd a teljes hozzászólást!
  • Na közben eléggé összezavarodtam. A mostani kódomban a külön szálban, az execute metódusának a végén ez van:

    { Terminate the Thread - This signals Terminated=True }
    Terminate;


    Ez itt igazából nem árt, de nem is használ. Simán elhagyhatod. A .Terminate() metódus ugyanis semmi mást nem csinál, csak TRUE-ba rakja a TThread Terminate flagjét. (Amit a while not terminated do részben figyelsz.)

    Arra a kóddarabra a TThread leszármazottad destruktorában lenne szükség, de a főszálban is jó.
    If Assigned(SecondThread) then Try { Terminate thread } SecondThread.Terminate; // <--- itt . . . { Free & NIL it } SecondThread.Free; SecondThread := NIL;

    Annyi, hogy a .Terminate() után "illik" hívni a száladra egy .WaitFor()-t.
    Mutasd a teljes hozzászólást!
  • Na közben eléggé összezavarodtam. A mostani kódomban a külön szálban, az execute metódusának a végén ez van:

    { Terminate the Thread - This signals Terminated=True } Terminate;
    Tehát ez a "while not terminated do" rész után van, teljesen az execute végén, (a freeonterminate false értékű).

    Amikor pedig leáll a program (szolgáltatás), akkor a főszálban (servicestopban, ill. shutdown) van egy ilyen: 

    If Assigned(SecondThread) then Try { Terminate thread } SecondThread.Terminate;
    . . . { Free & NIL it } SecondThread.Free; SecondThread := NIL;
    Persze ezeken kívül még van egyéb is, csak most a lényeget emeltem ki.
    És ennek jónak kéne lennie, mert valami elég korrekt oldal leírása alapján csináltam, bár azóta írkáltam hozzá, de úgy emlékszem, hogy ez így volt megadva alapból.
    Mutasd a teljes hozzászólást!
  • az még nem teljesen világos, hogy miért nem lehet a szál execute metódusában (illetve annak végén, a terminate utasítás előtt) felszabadítani (free-vel) egy TEvent objektumot, mikor ugyanúgy, az execute elején lett létrehozva


    Egyrészt a száladban (ami az objektumod .Execute() metódusa és semmi több) nincs .Terminate() hívás. Nem is lenne értelme, hogy legyen. Ott a futó kódod van. Ahol van .Terminate() hívás, az a destrutorod (vagy az, ahol létrehoztad az objektumodat). Az pedig nincs a száladban. Az a főszálban van.

    Másrészt, ha figyeltél, leírtam, hogy
    Delphi alatt, ha egy bármilyen (nem csak TThread utód) objektumpéldányra .Free()-t hívsz, akkor a desturktora (.Destroy()) fog lefutni.

    tehát ha a desturktorban .Free()-t hívsz, az nemkívánt rekurzióhoz vezet. (Gondolj a .Free()-re úgy, mintha egy alias lenne a .Destroy()-hoz!)

    A harmadik, hogy a TThread objektumod a szálad wrappere, így azt egyszerűen nem semmisítheted meg amíg a szálad fut. (Mert csak. Mert a kocsit is leállítod és kiveszed a gyujtáskulcsot mielőtt kiszálsz.)
    Mutasd a teljes hozzászólást!
  • Ismét köszönöm a részletes választ!
    Viszont az még nem teljesen világos, hogy miért nem lehet a szál execute metódusában (illetve annak végén, a terminate utasítás előtt) felszabadítani (free-vel) egy TEvent objektumot, mikor ugyanúgy, az execute elején lett létrehozva?
    Hiszen nem a szálat destroyolom, csak azt az egy objektumot.
    Mutasd a teljes hozzászólást!
  • Ha csak "simán" felaszabadítom a "Free" utasítással (FThreadHasWork.Free), az is jó? Csak mert ez csöppet egyszerűbb. Persze ez magában a szálban lenne, a terminate előtt.


    Nos... úgy érzem akad itt némi fogalmi yavar amit tisztázni illik mielőtt válaszolok:

    1) Delphi alatt, ha egy bármilyen (nem csak TThread utód) objektumpéldányra .Free()-t hívsz, akkor a desturktora (.Destroy()) fog lefutni.

    2) A destruktor lefutása után az objektum által lefoglalt összes memória (pl. az objektum mezői) "kifut a scope-ból", azaz a nem "kézzel" lefoglalt dinamikus változók kivételével felszabadul. Ebből a szempontból a virtuális metódusok is mezőnek számítanak.

    3) TThread leszármazottak esetében külön szálban kizárólag a .Execute() metódusban futó kód (illetve az onnan hívott egzéb függvények/metódusok) fog(nak) futni.

    A fentiek fényében...
    - A "szálból" csak akkor hívhatnál .Free()-t, ha az .Execute() metódusból hívnád azt, ami beláthatatlan következményekkel járna. (Maga az .Execute() metódus is virtuális.)

    - A .Destroy() metódusból .Free()-t hívva a .Destroy() metódus hívódik meg. Ami meg fogja hívni a .Destroy() metódust... elméletben az ilyet végtelen rekurziónak nevezzük, gyakorlatban azonban Stack Overflownak (veremtúlcsordulásnak).

    - Ahogy lentebb már írtam, egy szál szabályos befejezéséhez az .Execute() metódusnak vagy már a destruktor hívása (.Free()) előtt- vagy legkésőbb az alatt (.Destroy() metódus) de még az inherited Destroy() hívás előtt kell megtörténnie.
    Mutasd a teljes hozzászólást!
  • Ha csak "simán" felaszabadítom a "Free" utasítással (FThreadHasWork.Free), az is jó? Csak mert ez csöppet egyszerűbb.  Persze ez magában a szálban lenne, a terminate előtt.
    Mutasd a teljes hozzászólást!
  • Illetve még egy kérdés. A destruktoros - szabadon engedős rész alatt pontosan mit értesz?


    Egy TThread leszármazott megsemmisítésének(felszabadításának) a tiszta módja az, ha az execute metódus lefut mielőtt az
    inherited Destroy();

    meghívásra kerül. Ezt úgy tudod elérni, ha a destruktorban leállítod a szálat és kivárod, hogy le is álljon kb így:
    procedure YourTThreadDescendent.Destroy(); begin self.Terminate(); // this will inform your thread about the fact of destruction FThreadHasWork.Set(); // this will wake up your thread to be able to finish self.Waitfor(); // this will wait 'till the thread finishes (the .Execute() method ends) FThreadHasWork.Free(); // release your event of course // additional destructor code can be inserted here inherited Destroy(); // because .Execute() surely ended at the WaitFor() line. end;

    az Execute(); főciklusában pedig figyeled, hogy hívtak-e Terminate()-et kb így:
    procedure YourTThreadDescendent.Destroy(); begin while not Terminated do // This line will terminate your thread when .Terminate is called... begin { infinitely wait for FThreadHasWork to be set } FThreadHasWork.WaitFor(INFINITE); // ...but only if this event is set. // ... Perform tasks here... end; end;

    Viszont ahhoz hogy a szálad szabályosan le tudjon állni futnia kell. (Tehát FThreadHasWork.Set();-et kell hívnod a destruktorban (vagy előtte).)
    Mutasd a teljes hozzászólást!
  • Köszönöm válaszod!
    Valójában sejtettem, hogy az általam kigondolt megoldás nem lesz jó. 
    Amit írtál, jó megoldást, azt kipróbáltam, és úgy néz ki, működik. A FThreadHasWork változót a mellékszál public részében deklaráltam TEventnek, illetve a unitokhoz beraktam syncobjs-et.
    A mellékszál execute részében, a loop rész előtt írtam be a "FThreadHasWork := TEvent.Create" kreálást, így:

    FThreadHasWork := TEvent.Create( nil, // default security descriptor true, // manual reset event true, // initial state: set ''); // no name - nobody has anything to do with it except us . . . while not Terminated do begin { infinitely wait for FThreadHasWork to be set } FThreadHasWork.WaitFor(INFINITE); // ... Perform tasks here... end;
    A többi pedig ahogy írtad. Remélem így már tényleg jó lesz. 
    Illetve még egy kérdés. A destruktoros - szabadon engedős rész alatt pontosan mit értesz?
    Mutasd a teljes hozzászólást!
  • A Suspend/Resume nem olyasmi amit valójában csinálni szeretnél a már létrehozott Thread-jeiddel. Szálkezelésnél általános érvényű szabály, hogy egy thread-del nem "csinálunk" semmit (SuspendThread(), TerminateThread()) kívülről, hanem jelzünk neki, hogy mit várunk tőle. Kezdetnek azt mondanám, hogy simán csak hagyd szabadon futni a worker thread-edet (nyilván egy ciklusban várakozva a főszálra). Ha ez túl sok processzoridőt eszik, létezik egy könnyű és egy megoldás hogy ne tegye.

    A könnyű megoldás, hogy teszel a várakozó ciklusba egy Sleep()-et. Mikor a végrehajtás egy Sleep() utasításhoz érkezik, a feladatütemező átadja a vezérlést a következő várakozó szálnak, így a főszálad illetve a többi futó program lélekzethez jut. (A Sleep() paraméterében lévő szám azt jelzi a feladatütemező-nek, hogy a szálad legalább ennyi ideig nem akarja visszakapni a vezérlést. A Sleep(0) pl. azt jelenti, hogy "átadom a vezérlést a következő várakozó szálnak, és kérem vissza ha lehet". A Sleep(1000) pl. azt jelenti, hogy "átadom a vezérlést a következő várakozó szálnak, és nem is kérem vissza egy teljes másodpercig".)

    A megoldás, ha szinkronizációs objektumokat használsz. Ha pl. létrehozol egy (Windows) eseményt amivel "megengeded", hogy a thread-ed fusson, akkor kívülről leállthatod és újra is indíthatod. Valami olyasmire gondolok, hogy a TThread leszármazottad konstruktorában létrehozol egy eventet valahogy így:
    FThreadHasWork := TEvent.Create( nil, // default security descriptor true, // manual reset event true, // initial state: set ''); // no name - nobody has anything to do with it except us

    azután a ciklusban ahol a szálad várakozna a főszálra csinálsz egy ilyet:

    while not Terminated do begin { infinitely wait for FThreadHasWork to be set } FThreadHasWork.WaitFor(INFINITE); // ... Perform tasks here... end;

    ezután a főszálból tudod vezérelni a száladat:

    FThreadHasWork.Set(); // turn thread on FThreadHasWork.ReSet(); // turn thread off

    Még annyit, hogy ha ilyesmit csinálsz, ne felejtsd el a TThread-ed destruktorában "szabadon engedni" a száladat, hogy szabályosan be tudjon fejeződni.
    Mutasd a teljes hozzászólást!
  • Ez a suspend - resume megoldás megtetszett. Kérdés, hogy itt, ahol írtad az állapotgépes megoldást, mennyire szép megoldás az alábbi:
    A külön szál mikor végez, jelez a főszálnak (akár egy változóval), ami vagy suspendeli a mellékszálat, vagy hagyja magában "pörögni". Aztán ha megint szükség van rá, resumélja (kicsit furcsán fogalmaztam meg, de na..  ), illetve jelez a mellékszálnak (akár szintént egy változóval, ami lehet az előző..), hogy mit csináljon.
    Szerintem nem rosszabb, mint ha mindig megsemmisítenél, illetve létrehoznák egy új szálat, a folyamat állapotától függően.

    Szerk.: ha ez nem bolygatja szét az oprendszert, akkor ezt használnám, mert így elég jól kitaláltam a program struktúráját, és már jó lenne vele haladni... 
    Mutasd a teljes hozzászólást!
  • A helyzet itt is ugyanaz. Egy szálnak akkor nem kell szinkronizálva átadni az adatokat, ha biztos vagy benne, hogy "ő" és a főszál nem szimultán férnek hozzájuk. Ezt te pl. Így tudod elérni:

    1) Az összes adatot amivel a szálad dolgozik, átadod a TThread konstruktornak, és annak az elején az öröklött konstruktor-t TRUE paraméterrel hívod meg. (inherited Create(TRUE);) Ezzel azt éred el, hogy a szálad "alapból" nem indul el.

    2) Ezután másold az összes adatot az objektum privát mezőibe.

    3) A konstruktor végén hívd meg a .Resume(); függvényt. (Ez indítja el a száladat.)

    4) Mielőtt a szálad értesíti a főszálat, hogy végzett a futással, az eredményeket másold az ő publikus mezőibe, ahonnan a főszál biztonságosan ki tudja majd nyerni.

    Ha így jársz el, nem lesz szükséged explicit szinkronizációra a szálaid között.
    Mutasd a teljes hozzászólást!
  • Akkor valószínűleg itt nem kell szinkronizálni, mert ez egy szervízprogram, tehát nincs vizuális felülete. Annyi lehet még a bökkenő, hogy változó értékátadásnál (szálak között) mi a helyzet...
    Mutasd a teljes hozzászólást!
abcd