MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Clasa AutoResetEvent

Clasa AutoResetEvent servește și ea scopurilor de sincronizare a thread-urilor. Această clasă reprezintă un eveniment de sincronizare a thread-urilor, care permite la primirea unui semnal să schimbe starea obiectului eveniment dintr-o stare semnalizată într-o stare nesemnalizată.

Pentru gestionarea sincronizării, clasa AutoResetEvent oferă o serie de metode:

  • Reset(): setează starea nesemnalizată a obiectului, blocând thread-urile
  • Set(): setează starea semnalizată a obiectului, permițând unuia sau mai multor thread-uri în așteptare să-și continue activitatea
  • WaitOne(): setează starea nesemnalizată și blochează thread-ul curent până când obiectul AutoResetEvent primește un semnal

Evenimentul de sincronizare poate fi într-o stare semnalizată sau nesemnalizată. Dacă starea evenimentului este nesemnalizată, thread-ul care apelează metoda WaitOne va fi blocat până când starea evenimentului devine semnalizată. Metoda Set setează starea semnalizată a evenimentului.

Astfel, în unul dintre exemplele anterioare, pentru sincronizarea thread-urilor s-a utilizat operatorul lock:

int x = 0;
object locker = new();  // obiect placeholder

// pornim cinci thread-uri
for (int i = 1; i < 6; i++)
{
   Thread myThread = new(Print);
   myThread.Name = $"Thread {i}";
   myThread.Start();
}

void Print()
{
   lock (locker)
   {
       x = 1;
       for (int i = 1; i < 6; i++)
       {
           Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
           x++;
           Thread.Sleep(100);
       }
   }
}

Rescriem acest exemplu utilizând AutoResetEvent:

int x = 0;  // resursă comună

AutoResetEvent waitHandler = new AutoResetEvent(true);  // obiect-eveniment

// pornim cinci thread-uri
for (int i = 1; i < 6; i++)
{
   Thread myThread = new(Print);
   myThread.Name = $"Thread {i}";
   myThread.Start();
}

void Print()
{
   waitHandler.WaitOne();  // așteptăm semnalul
   x = 1;
   for (int i = 1; i < 6; i++)
   {
       Console.WriteLine($"{Thread.CurrentThread.Name}: {x}");
       x++;
       Thread.Sleep(100);
   }
   waitHandler.Set();  // semnalizăm că waitHandler este în stare semnalizată
}

În primul rând, creăm o variabilă de tip AutoResetEvent. Prin transmiterea valorii true în constructor, indicăm că obiectul creat va fi inițial în stare semnalizată.

Când începe să ruleze un thread, prima operație efectuată în metoda Print este apelul waitHandler.WaitOne(). Metoda WaitOne indică faptul că thread-ul curent este pus în stare de așteptare până când obiectul waitHandler va fi setat în stare semnalizată. Astfel, toate thread-urile sunt puse în stare de așteptare.

După finalizarea activității, se apelează metoda waitHandler.Set, care notifică toate thread-urile în așteptare că obiectul waitHandler este din nou în stare semnalizată, iar unul dintre thread-uri "capturează" acest obiect, îl trece în stare nesemnalizată și își execută codul. Celelalte thread-uri așteaptă din nou.

Deoarece în constructorul AutoResetEvent am indicat că obiectul este inițial în stare semnalizată, primul thread din coadă capturează obiectul și începe să-și execute codul.

Dar dacă am fi scris AutoResetEvent waitHandler = new AutoResetEvent(false), obiectul ar fi fost inițial în stare nesemnalizată, iar deoarece toate thread-urile sunt blocate de metoda waitHandler.WaitOne() în așteptarea semnalului, programul ar fi fost blocat și nu ar fi executat nicio acțiune.

Dacă în programul nostru sunt utilizate mai multe obiecte AutoResetEvent, putem folosi pentru monitorizarea stării acestor obiecte metodele statice WaitAll și WaitAny, care acceptă ca parametru un array de obiecte de tip WaitHandle - clasa de bază pentru AutoResetEvent.

Astfel, putem folosi și WaitAll în exemplul de mai sus. Pentru aceasta, trebuie să înlocuim linia:

waitHandler.WaitOne();

cu următoarea:

AutoResetEvent.WaitAll(new WaitHandle[] {waitHandler});
← Lecția anterioară Lecția următoare →