Lucrul cu fire în delphi este totul despre asta și despre programare

Toată lumea știe că Windows este un sistem multi-tasking. Pur și simplu, acest lucru înseamnă că mai multe programe pot rula simultan sub control OS. Cu toții am deschis managerul de sarcini și am văzut lista proceselor. Un proces este o instanță a unei aplicații executabile. De fapt, de la sine nu funcționează, este creat atunci când aplicația începe, conține informații de proprietate, prin care sistemul este de lucru cu el, așa a făcut el să aloce memoria necesară pentru codul și date. Pentru ca programul să funcționeze, creează un fir. Orice proces conține cel puțin un fir și acesta este responsabilul pentru executarea codului și primește timp CPU pentru acest lucru. Aceasta oferă, de asemenea, o operare imaginară paralelă de programe sau, așa cum se mai numește, pseudo-paralelism. De ce imaginar? Deoarece procesorul poate executa o singură bucată de cod la un moment dat. Windows distribuie timpul procesorului la toate firele din sistem la rândul său, creând astfel impresia că funcționează simultan. Firele reale care rulează în paralel pot fi numai pe mașini cu două sau mai multe procesoare.

Pentru a crea fire adiționale în Delphi, există o clasă de bază, TThread, din care vom moșteni atunci când implementăm firele noastre. Pentru a crea un "schelet" al unei noi clase, poti selecta meniul File - New - Object Thread, Delphi va crea un nou modul cu achizitia acestei clase. Din motive de claritate, o voi descrie în modulul de formă. După cum puteți vedea, în această piesă se adaugă o metodă - Executați. Aceasta este ceea ce trebuie să redefinăm, codul din interior va funcționa într-un fir separat. Și astfel, să încercăm să scriem un exemplu - să conducem o buclă infinită în fir:
Rulați proba și faceți clic pe. Se pare că nu se întâmplă nimic - forma nu atârnă, ci reacționează la mișcări. De fapt, acest lucru nu este așa - deschideți managerul de sarcini și veți vedea că procesorul este încărcat în întregime. Acum, în procesul de aplicare sunt două fire - una a fost creată inițial, când a fost lansată aplicația. Al doilea, care încarcă procesorul - am creat făcând clic pe buton. Deci, să vedem ce înseamnă codul în Button1Click:
Aici am creat o instanță a clasei TNewThread. Creați un constructor are doar o singură opțiune - CreateSuspended tipul boolean, care indică începe un nou fir imediat după crearea (dacă este fals), sau așteptați comanda (dacă este adevărat).
proprietatea FreeOnTerminate determină că firul se va termina automat după executare, obiectul va fi distrus și nu trebuie să îl distrugem manual. În exemplul nostru, acest lucru nu contează, deoarece în sine nu se va încheia niciodată, dar va fi necesar în următoarele exemple.
Proprietatea Prioritate, dacă nu ați ghicit din nume, stabilește prioritatea firului. Da, da, fiecare fir din sistem are prioritate proprie. Dacă timpul procesorului nu este suficient, sistemul începe să îl aloce în funcție de prioritățile firelor. Proprietatea Prioritară poate lua următoarele valori:
  • tpTimeCritical - Critic
  • tpHighest - foarte mare
  • tpHigher - înalt
  • tpNormal - mediu
  • tpLower - scăzut
  • tpLowest - foarte scăzut
  • tpIdle - firul rulează în timpul întreruperii sistemului
Nu merită să setați fire cu prioritate ridicată dacă acest lucru nu este cerut de sarcină, deoarece încarcă foarte mult sistemul.
Ei bine, de fapt, lansarea fluxului.

Cred că acum înțelegeți cum sunt create fluxurile. Observați, nimic complicat. Dar nu este așa de simplu. S-ar părea - scriem orice cod în cadrul metodei Execute și totul, dar nu, firele au o proprietate neplăcută - nu știu nimic despre ele. Și ce e în neregulă cu asta? - întrebi. Iată ce: presupuneți că încercați să schimbați proprietatea unei componente pe un formular dintr-un alt fir. După cum știți, VCL este un singur filet, tot codul din cadrul aplicației este executat secvențial. De exemplu, în procesul de orice schimbat date în cadrul claselor VCL, sistemul selectează în momentul în care fluxul principal trece în jurul valorii de alte fluxuri și se întoarce înapoi, executarea de cod continuă de la punctul în care au stagnat. Dacă suntem de schimbare a fluxului lor ceva, de exemplu, sub formă, este implicat în mai multe mecanisme de VCL (Vă reamintesc de fluxul de bază acum „suspendat“), respectiv, în această perioadă va avea timp să se schimbe date. Și apoi brusc timpul este dat din nou firului principal, acesta continuă să continue fără probleme, dar datele au fost deja schimbate! Ce poate duce la aceasta - nu poate fi prevăzută. Puteți testa acest lucru de o mie de ori și nu se va întâmpla nimic, dar pentru prima oară, primul program se va prăbuși. Și aceasta se aplică nu numai interacțiunii fluxurilor suplimentare cu principalele, ci și interacțiunii fluxurilor între ele. Desigur, nu puteți scrie astfel de programe nesigure.

Sincronizarea firelor

Probabil ați observat că în interiorul bucla folosim procedura Sleep. Într-o aplicație cu un singur file, Sleep este rar folosit, dar este foarte convenabil să-l utilizați în fire. Un exemplu este o buclă infinită, până când o anumită condiție este îndeplinită. Dacă nu puneți Sleep acolo, vom încărca sistemul cu o muncă inutilă.

Aici, în principiu, am luat în considerare modalitățile de bază de lucru cu componente VCL din fire. Și dacă dacă mai există un fir nou în programul nostru? Și trebuie să organizați lucrul cu aceleași date? Aici, alte metode de sincronizare vin în ajutorul nostru. Vom lua în considerare unul dintre ei. Pentru ao implementa, trebuie să adăugați modulul SyncObjs la proiect.

Secțiuni critice

Acestea funcționează după cum urmează: numai un fir poate funcționa în secțiunea critică, în timp ce alții așteaptă finalizarea acestuia. Pentru a înțelege mai bine toate comparațiile sunt realizate cu o țeavă îngustă, imaginați-vă, pe de o parte, fluxurile de „mulțime“, dar conducta poate „urca“ singurul, și atunci când el „trece prin“ - începe a doua mișcare, și așa mai departe, în ordine. Este chiar mai ușor să înțelegeți acest lucru cu exemplul aceluiași ProgressBar. Deci, rulați unul dintre exemplele date mai devreme. Apăsați butonul, așteptați câteva secunde, apoi apăsați din nou. Ce se întâmplă? ProgressBar a început să sară. Sare pentru că nu avem un fir, ci două, iar fiecare dintre ele transmite diferite valori ale progresului. Acum vom re-codul puțin, în evenimentul onCreate al formularului vom crea o secțiune critică:
TCriticalSection are două metode de care avem nevoie, introduceți și lăsați, respectiv, intrarea și ieșirea din ea. Puneți codul nostru în secțiunea critică:
Încercați să rulați aplicația și faceți clic pe buton de mai multe ori și apoi să numărați de câte ori va fi făcut progresul. Este clar, care este esența? Prima dată, făcând clic pe buton, creăm un flux, ocupă secțiunea critică și începe să lucreze. Apăsăm al doilea - este creat un al doilea fir, dar secțiunea critică este ocupată și așteaptă până când prima îl eliberează. Al treilea, al patrulea - toate vor trece numai la rândul lor.

Secțiunile critice sunt utile atunci când procesează aceleași date (liste, matrice) prin fire diferite. Înțelegând cum funcționează, le veți găsi mereu în uz.

În acest articol mici nu acoperă toate metodele de sincronizare, există evenimente (TEvent), precum și sistem de obiecte, cum ar fi mutex (Mutex), semafoarele (Semafor), dar ele sunt mai potrivite pentru comunicarea între aplicații. Ceilalți, în ceea ce privește utilizarea clasei TThread, puteți să aflați că, în ajutor, totul este descris în detaliu. Scopul acestui articol este de a arăta începătorilor că nu totul este atât de complicat și înfricoșător, principalul lucru este să dau seama ce este. Și mai multă practică - cea mai importantă experiență!

Articole similare