Fluxuri și metode de sincronizare a acestora în delphi, diferite articole, articole, programare -

Când aplicația începe, sistemul creează automat un fir de execuție. Prin urmare, dacă aplicația are un singur fir, atunci tot codul va fi executat secvențial, ținând cont de toate tranzițiile condiționate și necondiționate.

Multithreading oferă o operație pseudo-paralelă a mai multor programe. În unele cazuri, nu puteți face fără crearea de fire, de exemplu, atunci când lucrați cu socket-uri în modul de blocare.

În Delphi, există o clasă specială care implementează firele - tthread. Aceasta este clasa de bază din care trebuie să moșteniți clasa și să înlocuiți metoda de execuție.

Acum puteți scrie cod în corpul procedurii tnew.execute, execuția pe care programul o va agăța.

Moment subtil. În corpul procedurii, nu trebuie să apelați metoda unui strămoș.

Acum trebuie să începeți firul. La fel ca în fiecare clasă, trebuie să creați:

Valoarea adevărată în metoda de creare înseamnă că, după crearea clasei, firul nu va porni automat.

Apoi, specificăm că, după terminarea codului de flux, se va termina imediat, adică nu-mi pasă de închidere. În caz contrar, trebuie să apelați singur funcția de terminare.

Setați prioritatea la una dintre valorile posibile:

tpidle Funcționează atunci când sistemul este inactiv
Cel mai mic tplowest
tplower Low
Normal Normal
tigherul înalt
cel mai tare
Criterii critice

Nu recomand să setați o prioritate prea mare. firul poate încărca sistemul în mod semnificativ.

Moment subtil. Dacă există o buclă infinită de procesare în flux, firul va încărca sistemul sub șir. Pentru a evita acest lucru, introduceți funcția sleep (n), unde n este numărul de milisecunde la care firul va întrerupe când întâlnește această funcție. n ar trebui să fie aleasă în funcție de problema rezolvată.

Apropo, dacă intenționați să scrieți codul de flux într-un modul separat, puteți ușor simplifica scrierea unui schelet de clasă. Pentru aceasta, selectați obiectul thread din depozitul de obiecte (Acesta este în fila nouă). Va apărea o fereastră în care trebuie să introduceți numele clasei, după care, făcând clic pe OK, va fi creat automat un nou modul cu scheletul clasei dvs.


Sincronizarea firelor atunci când accesați componentele vcl

Așa că am învățat cum să creăm fire. Dar apoi apare un lucru interesant: ce se întâmplă dacă două fire se referă la aceleași date prin înregistrare? De exemplu, două fire încearcă să schimbe antetul din formularul principal.

În special pentru aceasta, mecanismele de sincronizare sunt implementate în sistemul de operare. În particular, în clasa a treia există o metodă care permite evitarea accesului paralel la componentele vcl:

sincronizare de procedură (metoda: tthreadmethod);

Iată un exemplu complet în care metoda addstr adaugă câteva rânduri la memo. Dacă apelăm doar la metoda, liniile din fluxuri vor fi adăugate în ordine aleatorie. Dacă addstr se numește sincronizați, atunci liniile sunt adăugate mai întâi dintr-un fir și apoi din al doilea. Se pare că firul captează exclusiv memoria de resurse și adaugă informațiile necesare, după adăugarea firului lansează nota și acum un alt fir poate adăuga memo-ul său în memo. Cuvinte mai putine - mai multe suruss:

utilizări
ferestre, mesaje, sysutils, variante, clase, grafică, comenzi, forme, dialoguri, stdctrls;

tnew = clasa (tthread)
privat
s: șir;
procedura addstr;
protejat
executarea procedurii; suprascrie;
se încheie;

var
forma 1: forma 1;
new1, new2: tnew;

procedură tform 1.button1click (expeditor: tobject);
începe
new1: = tnew.create (adevărat);
new1.freeonterminate: = true;
new1.s: = '1 fir';
new1.priority: = tplowest;
new2: = tnew.create (adevărat);
new2.freeonterminate: = adevărat;
new2.s: = '2 fire';
new2.priority: = tptimecritical;
new1.resume;
new2.resume;
se încheie;

procedura tnew.execute;
începe
sincronizați (addstr); // Apelați metoda cu sincronizare
// addstr; // Apelați metoda fără sincronizare
se încheie;

Alte modalități de sincronizare. Modulul syncobjs

În modulul syncobjs există clase de sincronizare, care sunt împachetări ale apelurilor funcției api. În total, în acest modul sunt declarate cinci clase. tricticalsection, tevent, precum și o implementare mai simplă a clasei tevent-tsimpleevent și sunt folosite pentru sincronizarea firelor, celelalte clase nu pot fi luate în considerare. Aici este ierarhia de clasă din acest modul:

Secțiuni critice de secțiune tricică

Cea mai simplă înțelegere este secțiunea tricică sau secțiunea critică. Codul aflat în secțiunea critică poate fi executat numai printr-un fir. În principiu, codul nu este alocat, ci apeluri la cod prin secțiunea critică. La începutul codului este funcția de intrare în secțiune și la sfârșitul ieșirii din secțiune. Dacă secțiunea este ocupată de un alt fir, firele așteaptă până când se eliberează secțiunea critică.


La începutul lucrării trebuie creată secțiunea critică:


var
secțiune: secțiune tricică; // variabilă globală
începe
section.create;
se încheie;

Să presupunem că există o funcție în care elementele sunt adăugate la matricea globală:

Să presupunem că mai multe fire de apel numesc această funcție, astfel încât, pentru a evita un conflict de date, puteți folosi secțiunea critică după cum urmează:

funcția addelem (i: integer);
var
n: întreg;
începe
section.enter;
n: = lungime (mas);
lungimea setului (mas, n + 1);
mas [n + 1]: = i;
section.leave;
se încheie;

Voi specifica faptul ca pot exista cateva sectiuni critice. Prin urmare, atunci când se utilizează mai multe funcții în care pot exista conflicte de date, este necesar ca fiecare funcție să creeze o secțiune critică proprie. După terminarea utilizării, atunci când funcțiile nu mai sunt solicitate, secțiunile trebuie să fie distruse.

După cum înțelegeți, sper foarte mult că intrarea și ieșirea din secțiunea critică nu trebuie să fie într-o singură funcție. Înscrierea indică numai faptul că un alt fir care întâlnește intrarea și care își găsește ocupația va fi suspendat. Și ieșirea eliberează pur și simplu intrarea. Pur și simplu, secțiunea critică poate fi reprezentată ca o conductă îngustă pentru un flux, de îndată ce fluxul se apropie de țeavă, se uită în ea și dacă vede că cineva urcă deja prin conductă, va aștepta până ce celălalt va ieși.

Iată un exemplu în care un element este adăugat unei matrice dinamice. Funcția de somn adaugă o întârziere la buclă, ceea ce vă permite să vedeți vizual conflictul asupra datelor, dacă, desigur, eliminați intrarea și ieșirea din secțiunea critică din cod.

utilizări
ferestre, mesaje, sysutils, variante, clase, grafică, controale, forme, dialoguri, stdctrls, syncobjs;

tip
forma 1 = clasa (forma)
butonul1: butonul t;
memo1: tmemo;
formularul procedurii crea (expeditor: tobject);
distrugerea procedurii (expeditor: tobject);
butonul de procedură1click (expeditor: tobject);
privat

public

se încheie;

tnew = clasa (tthread)
protejat
executarea procedurii; suprascrie;
se încheie;

var
forma 1: forma 1;
cs: secțiune tricică;
new1, new2: tnew;
mas: mulțimea întregului;

procedura de formular 1.form crea (expeditor: tobject);
începe
setlength (mas, 1);
mas [0]: = 6;
// Creați secțiunea critică
cs: = tricticalsection.create;
se încheie;

procedura deformare 1.form distruge (expeditor: tobject);
începe
// Ștergeți secțiunea critică
cs.free;
se încheie;

procedură tform 1.button1click (expeditor: tobject);
începe
new1: = tnew.create (adevărat);
new1.freeonterminate: = true;
new1.priority: = tpidle;
new2: = tnew.create (adevărat);
new2.freeonterminate: = adevărat;
new2.priority: = tptimecritical;
new1.resume;
new2.resume;
se încheie;

Pentru începători, nu prea multe despre funcțiile de așteptare. Acestea sunt funcții care suspendă execuția firului. Un caz particular al funcției de așteptare este somnul, ca argument, numărul de milisecunde la care doriți să înghețați sau să întrerupeți fluxul.

Moment subtil. Dacă sună somnul (0), firul va abandona ciclul de ceas - timpul procesorului și va intra imediat în coada de așteptare cu o disponibilitate de a executa.

Funcția de așteptare completă este trecută ca parametri pentru descriptorii de fire. Nu voi mai vorbi în detaliu acum. În principiu, funcțiile de așteptare încapsulează anumite clase de sincronizare într-o formă explicită, restul nu este explicit.

Evenimentele Tevent pot fi utilizate nu numai într-o aplicație multi-filetată, ci și într-un singur fir ca o coordonare între secțiunile de cod și atunci când se transferă date de la o aplicație la alta. În aplicațiile multi-filetate, utilizarea teventului pare mai rezonabilă și mai ușor de înțeles.

Totul se întâmplă după cum urmează. Dacă evenimentul este setat, atunci este posibil să lucrați mai mult dacă evenimentul este resetat, atunci toate celelalte fire sunt în așteptare. Diferența dintre evenimente și secțiunile critice este că evenimentele sunt verificate în codul firului însuși, iar funcția de așteptare este utilizată în mod explicit. Dacă funcția de așteptare a fost executată automat în secțiunea critică, atunci când se utilizează evenimente este necesar să se apeleze pentru a îngheța fluxul.

Evenimentele se întâmplă cu o resetare automată și fără o resetare automată. Cu autoset, înseamnă că evenimentul este resetat imediat după revenirea din funcția de așteptare. Dacă utilizați evenimente fără auto-reinițializare, trebuie să le resetați singuri.

Un eveniment fără auto-resetare este util pentru întreruperea într-o anumită zonă a codului de flux. Pauza doar în flux, când nu contează unde se produce înghețarea, puteți utiliza metoda tthread.suspend. Pot fi utilizate evenimente cu auto-reset, precum și secțiuni critice.

Pentru a începe, este necesar să creați un eveniment și, de preferat, înainte ca firele care le utilizează să fie create, deși este mai precis să apelați funcția de așteptare.

creați (eventattributes: psecurityattributes; manualreset, initialstate: boolean; const: string);

eventattributes - ia nul.
manual resetare - resetare automată - falsă, fără resetare automată - adevărat.
initialstate - stare inițială reală - set, false - resetare.
const numele este numele evenimentului, setați-l să fie gol. Un eveniment cu un nume este necesar atunci când schimbul de date între procese.

var
eveniment: tevent;
new1, new2: tnew; // fluxuri
...
începe
eveniment: = tevent.create (zero, false, false, '');
se încheie;
procedura tnew.execute;
var
n: întreg;
începe
event.waitfor (infinit);
n: = lungime (mas);
lungimea setului (mas, n + 1);
mas [n + 1]: = i;
event.setevent;
se încheie;

Toți acum nu vor exista greșeli.

Cel mai ușor de utilizat este clasa tsimpleevent, care este moștenitorul lui tevent și diferă de acesta doar prin faptul că constructorul său numește imediat constructorul strămoșilor cu parametrii stabiliți:

creați (zero, adevărat, fals, '');

De fapt, tsimpleevent este un eveniment fără o resetare automată, cu o stare resetată și fără nume.

Următorul exemplu prezintă modul de suspendare a executării unui fir dintr-o anumită locație. În acest exemplu, există trei bare de progres pe formular, firul umple bara de progres. Dacă doriți, puteți întrerupe și relua bara de progres. După cum înțelegeți, vom crea un eveniment fără o resetare automată. Deși este mai potrivit să folosim tsimpleevent, am folosit tevent de atunci stăpânirea muncii cu tevent va merge pur și simplu la tsimpleevent.

utilizări
ferestre, mesaje, sysutils, variante, clase, grafica, comenzi, forme, dialoguri, stdctrls, syncobjs, comctrls;

tip
forma 1 = clasa (forma)
butonul1: butonul t;
progresbar1: tprogressbar;
progresbar2: tprogressbar;
progresbar3: tprogressbar;
butonul2: butonul t;
formularul procedurii crea (expeditor: tobject);
distrugerea procedurii (expeditor: tobject);
butonul de procedură1click (expeditor: tobject);
butonul de procedură2click (expeditor: tobject);
privat

public

se încheie;

tnew = clasa (tthread)
protejat
executarea procedurii; suprascrie;
se încheie;

var
forma 1: forma 1;
nou: tnew;
eveniment: tevent;

procedura de formular 1.form crea (expeditor: tobject);
începe
// Creați un eveniment înainte să îl folosim
eveniment: = tevent.create (zero, adevărat, adevărat, '');
// Începeți firul
nou: = tnew.create (true);
new.freeonterminate: = true;
new.priority: = tplowest;
new.resume;
se încheie;

procedura deformare 1.form distruge (expeditor: tobject);
începe
// Ștergeți evenimentul
event.free;
se încheie;


procedura tnew.execute;
var
n: întreg;
începe
n: = 0;
în timp ce adevărat
începe
// așteptați funcția
event.waitfor (infinit);
dacă n> 99 atunci
n: = 0;
// Creșteți simultan
form 1.progressbar1.position: = n;
form 1.progressbar2.position: = n;
formularul 1.progressbar3.position: = n;
// întârziere pentru vizibilitate
somn (100);
inc (n)
se încheie;
se încheie;

procedură tform 1.button1click (expeditor: tobject);
începe
// Setați evenimentul
Funcția // wait va reveni imediat la control
event.setevent;
se încheie;

procedură tform 1.button2click (expeditor: tobject);
începe
// funcția de așteptare blochează executarea codului de flux
event.resetevent;
se încheie;


Un exemplu de utilizare a unui eveniment de auto-reset este lucrarea a două fire și funcționează după cum urmează. Un fir pregătește datele și un alt fir, după ce datele sunt gata, de exemplu, le trimite la server sau oriunde. Se pare ceva ca o lucrare secvențială.

utilizări
ferestre, mesaje, sysutils, variante, clase, grafica, comenzi, forme, dialoguri, stdctrls, syncobjs, comctrls;

tproc = clasa (tthread)
protejat
executarea procedurii; suprascrie;
se încheie;

tsend = clasa (tthread)
protejat
executarea procedurii; suprascrie;
se încheie;

var
forma 1: forma 1;
proc: tproc;
trimite: tsend;
eveniment: tevent;

procedura de formular 1.form crea (expeditor: tobject);
începe
// Creați un eveniment înainte să îl folosim
eveniment: = tevent.create (zero, fals, adevărat, '');
// Porniți firele
proc: = tproc.create (adevărat);
proc.freeonterminate: = true;
proc.priority: = tplowest;
proc.resume;
trimite: = tsend. creați (adevărat);
trimite. freeonterminate: = adevărat;
trimite. prioritate: = tplowest;
trimite. relua;
se încheie;

procedura deformare 1.form distruge (expeditor: tobject);
începe
// Ștergeți evenimentul
event.free;
se încheie;

Asta e tot modulul de sincronizare a obiectelor de sincronizare, care în principiu este suficient pentru a rezolva diferite probleme. În ferestre, există alte obiecte de sincronizare, care pot fi de asemenea folosite în delphi, dar deja la nivelul api. Acestea sunt mutexurile mutex, semaforul semafor și cronometrele așteptate.