Komunikace mezi thready

Pokročilé řadicí algoritmy

SSŠVT


Komunikace mezi thready

BackgroundWorker

Při vývoji aplikací typu WinForms používáme okna, popisky, vstupní textová pole, tlačítka atd. Celá aplikace je řízena událostmi. Například kliknutím na tlačítko dojde k události, která změní text nějakého popisku.

Pokud obsluha nějaké události (např. obsluha kliknutí na tlačítko DownloadDataButton) trvá moc dlouho (data se stahují z internetu, je to třeba soubor o velikosti několika set MB), uživateli se může zdát, že aplikace "zamrzla". Aplikace totiž nereaguje na jeho kliknutí apod., protože pomalá metoda události pro stažení dat ještě nedoběhla.

Abychom se vyhnuli pocitu uživatele, že GUI aplikace přestalo reagovat, je možné spustit časově náročný výpočet (stahování velkého objemu dat) ve vedlejším vlákně, tzv. background thread, neboli vlákně běžícím na pozadí. Hlavní vlákno, které obsluhuje události, se tak "nezasekne" v dlouho běžícím handleru a uživatel je spokojený: aplikace reaguje na jeho akce s myší nebo klávesnicí.

Problém nastane, když chce vedleší vlákno dát uživateli vědět, že se něco stalo. Např. chceme zobrazit info: Do stažení celého souboru zbývají 2 minuty a 45 sekund. A tuto informaci chceme pravidelně aktualizovat, aby měl uživatel přehled, za jak dlouho zhruba bude soubor kompletně stažený.

Oknová appka napsaná v .NET-u běží v operačním systému Windows v tzv. Single Thread Apartment neboli STA, což znamená, že se předpokládá, že s oknovými ovládacími prvky (tlačítka, textová pole apod.) interaguje pouze jediné (tzv. hlavní, někdy též foreground thread) vlákno aplikace. Pokud by se např. o zapsání textu do labelu nebo text boxu pokusilo jiné vlákno, mohlo by dojít k porušení datové integrity aplikace a jejímu nepředvídatelnému chování.

Aby mohl programátor použít v režímu STA více vláken a zároveň aby nedocházelo k výše popsaným chybám, nabízí .NET třídu BackgroundWorker. Tato třída umožňuje bezpečné předávání dat mezi hlavním vláknem (běžícím na popředí) a vedlejšími vlákny (běžícími na pozadí) tak, aby bylo informace od vedlejších vláken možné zobrazit třeba na nějakém formulářovém panelu nebo v text boxu.

Tři nejdůležitější události, které třída BackgroundWorker nabízí, jsou tyto:

Událost V jakém kontextu běží K čemu je
DoWork Na pozadí (background thread) Veškerý kód pomalu běžící operace, která by jinak zdržovala odpovědi na akce uživatele v GUI (kliknutí myší)
ProgressChanged Na popředí (foreground thread) Zobrazení progresu operace, která běží na pozadí (např. kolik procent celé operace již bylo dokončeno)
RunWorkerCompleted Na popředí Zobrazení výsledku operace, která běžela na pozadí (např. "Operace přerušena uživatelem", "Vyskytla se chyba", nebo "OK, hotovo!")

Vedlejší vlákno, které poběží na pozadí a bude vykonávat kód "pomalé" operace, se aktivuje voláním metody RunWorkerAsync na objektu BackgroundWorker.

Celý průběh dlouho trvající operace může vypadat např. takto:

  1. Uživatel klikne na tlačítko Start.
  2. Obsluha události Click na tlačítku Start spustí vedlejší vlákno voláním metody RunWorkerAsync objektu BackgroundWorker.
  3. Začne se provádět kód obsluhy události DoWork objektu BackgroundWorker ve vedlejším vlákně.
  4. V obsluze DoWork zavolá vedlejší vlákno občas metodu ReportProgress objektu BackgroundWorker, aby dalo hlavnímu vláknu update o postupu operace.
  5. Při každém zavolání metody ReportProgress dojde k události ProgressChanged na objektu BackgroundWorker.
  6. V obsluze události ProgressChanged může hlavní vlákno zobrazit informaci o postupu operace např. pomocí ovládacího prvku ProgressBar.
  7. Jakmile doběhne vedlejší vlákno, tj. dokončí se provádění obslužné metody pro událost DoWork, objekt BackgroundWorker "zvedne" událost RunWorkerCompleted.
  8. V obsluze události RunWorkerCompleted zobrazí hlavní vlákno uživateli informaci o výsledku operace, která běžela na pozadí.

Dokumentace na Microsoftu: https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker

Ukázka použití objektu BackgroundWorker ke stažení.


Semafor

Dokumentace na Microsoftu: https://docs.microsoft.com/en-us/dotnet/api/system.threading.semaphore