Poznáváme C# a Microsoft .NET – 49. díl – použití vláken

V minulém díle seriálu jsme se seznámili s pojmem vlákno a více vláknová aplikace. V dnešním díle na tyto znalosti navážu a pokusím se je rozšířit o další informace týkající se použití vláken v .NET.

Zjištění informací o vláknu

Z předchozího dílu víme, že jednotlivá vlákna, která jsou spouštěna v prostředí CLR, náležící do určité aplikační domény, jsou nám programátorům přístupna skrze instance třídy System.Threading.Thread. Také bychom si měli pamatovat, že k instanci představující aktuální vlákno, v němž se nacházíme, přistoupíme užitím statické vlastnosti CurrentThread na této třídě. Po použití této vlastnosti jsme schopni na odkazu na vlákno zjistit pomocí jeho instančních vlastností různé informace. Použití pár těchto vlastností je ukázáno v tomto zdrojovém kódu.

/// <summary>
/// Ukazka pouziti nekterych vlastnosti tridy Thread pro zjisteni
/// uzitecnych informaci.
/// </summary>
public class ThreadInfoExam
{
  internal static void Run()
  {
    Console.WriteLine("ID vlakna : {0}", Thread.CurrentThread.GetHashCode());
    Console.WriteLine("Aplikacni domena do ktere vlakno nalezi : {0}", Thread.GetDomain().FriendlyName);
    Console.WriteLine("ID aplikacni domeny do ktere vlakno nalezi : {0}", Thread.GetDomainID());
    Console.WriteLine("Priorita vlakna : {0}", Thread.CurrentThread.Priority);
    Console.WriteLine("Stav vlakna : {0} ", Thread.CurrentThread.ThreadState);
    Console.WriteLine("Kultura vlakna : {0}", Thread.CurrentThread.CurrentCulture.DisplayName);
  }
}

Po spuštění příkladu byste na svých obrazovkách měli vidět výstup velmi podobný tomu následujícímu.

ID vlakna : 2
Aplikacni domena do ktere vlakno nalezi : PrikladyZive49.exe
ID aplikacni domeny do ktere vlakno nalezi : 1
Priorita vlakna : Normal
Stav vlakna : Running
Kultura vlakna : Czech (Czech Republic)

Takže jak můžeme vidět, jsme schopni zjistit, jaké je ID vlákna i v jaké aplikační doméně je dané vlákno spuštěno. Kromě toho jsme schopni zjistit jaká je priorita vlákna, čehož jsme dosáhli přečtením vlastnosti Priority. Tato informace je důležitá pro část operačního systému nesoucí jméno plánovač. Plánovač přiřazuje jednotlivým vláknům časové úseky procesoru a právě na prioritě vlákna závisí to, jak velké tyto úseky budou.

Každé nově spuštěné vlákno v běhovém prostředí CLR je spuštěno s prioritou Normal, což je jedna z hodnot výčtu ThreadPriority. Vlastnost Priority na instanci vlákna můžeme nejen číst, ale také v případě potřeby nastavit na jednu z hodnot zmíněného výčtu. Každé vlákno se nachází v nějakém stavu, který jsme schopní zjistit přečtením vlastnosti ThreadState. Tato vlastnost vrací hodnotu patřící do stejnojmenného výčtového typu tedy také ThreadState. Běžící vlákno má hodnotu této vlastnosti Running.

Uspání vlákna

V případě potřeby můžeme zajistit, že na nějakou dobu nebude určitému vláknu přidělen plánovačem žádný časový úsek procesoru, jinak se také tomuto jevu říká uspání. Pokud chceme tak učinit, můžeme použít statické metody Sleep, která zapříčiní toto uspání na aktuálním vláknu na námi specifikovanou dobu. Kdybychom chtěli vlákno tímto způsobem zablokovat na nekonečnou dobu, předali bychom metodě jako parametr konstantu System.Threading.Timeout.Infinite.

/// <summary>
/// Ukazka pouziti metody Sleep tridy Thread
/// </summary>
public class SleepingExam
{
  internal static void Run()
  {
    Console.WriteLine("Vlakno si dava pauzu");
    Console.WriteLine(DateTime.Now.ToString());
    //zablokujeme nase vlakno na dobu 3 vterin
    Thread.Sleep(3000);
    Console.WriteLine("Vlakno opet bezi");
    Console.WriteLine(DateTime.Now.ToString());
  }
}

Vlákna s parametry

V mnoha případech při vývoji našich aplikací zajisté budeme potřebovat nejen spustit nějakou operaci ve vlastním vláknu, ale také budeme potřebovat předat k provádění této operace nějaké parametry. Dobrá, jakým způsobem, ale tuto situaci vyřešíme, když delegát ThreadStart, předepisuje metodu o nulovém počtu formálních parametrů? Řešení takovýchto situací se nám nabízí v podobě vytvoření třídy, jejíž instanční datové členy budou uchovávat hodnoty parametrů a také bude obsahovat metodu asociovatelnou s instancí delegáta ThreadStart, tedy metodu s návratovým typem void a žádnými formálními parametry. Následující zdrojový kód demonstruje použití tohoto přístupu v praxi.

/// <summary>
/// Trida jejiz instancni metoda bude spustena v jinem vlakne
/// v ukazkovem prikladu.
/// </summary>
public class ThreadWithParams
{
//datove cleny, ktere, ktere budou vyuzity
       //metodou spoustenou asynchronne (separatnim vlaknem)
  private int offset;
  private int length;
   
  public ThreadWithParams(int Offset, int Length)
  {
    this.offset = Offset;
    this.length = Length;
  }

  /// <summary>
  /// Metoda vykonavajici operace, ktere je potreba ucinit
  /// v separatnim vlaknu
  /// </summary>
  public void PerformActions()
  {
    Console.WriteLine("Pouzivam parametry : Offset = {0}, Delka = {1}", offset, length);
    for (int i = offset; i <= offset + length; i++)
    {
      Console.WriteLine("Vystup vlakna {0} : {1}", Thread.CurrentThread.Name, i);
    }
  }
}

Tato třída obsahuje instanční metodu PerformActions, která bude asociována s delegátem ThreadStart. Metoda využívá hodnot dvou instančních parametrů offset a length, které určují způsob běhu for cyklu, který metoda obsahuje. Takže k předání hodnot, které budou představovat hodnoty těchto parametrů, musíme před spuštěním vlákna vytvořit instanci této třídy pomocí implementovaného parametrického konstruktoru a po té asociovat zmíněnou metodu PerformActions s instancí delegáta ThreadStart.

Ono by to dříve než po vytvoření instance ani nešlo , jelikož je metoda instanční a z důvodu existence jediné verze konstruktoru, kterou je verze s parametry je zajištěno, že hodnoty kýženým parametrům budou přiřazeny vždy před spuštěním metody PerformActions ve vlastním vláknu. Vlastní spuštění vlákna tedy proběhne následujícím způsobem.

/// <summary>
/// Priklad na spusteni vlakna s predanim parametru
/// </summary>
public class ThreadWithParamsExam
{
  internal static void Run()
  {
    //vytvoreni instance tridy, ktera obsahuje metodu
            //spoustenou vlaknem
    ThreadWithParams lWorker = new ThreadWithParams(5,100);
    //asociace instancni metody s instanci delegata
    Thread lSecondThread = new Thread(new ThreadStart(lWorker.PerformActions));
    lSecondThread.Name = "Druhe vlakno";
    Console.WriteLine("Spoustim druhe vlakno..");
    //spusteni vlakna
    lSecondThread.Start();
  }
}

Na vašich obrazovkách byste měli vidět takovýto výstup.

Spoustim druhe vlakno..
Pouzivam parametry : Offset = 5, Delka = 100
Vystup vlakna Druhe vlakno : 5
Vystup vlakna Druhe vlakno : 6
Vystup vlakna Druhe vlakno : 7
Vystup vlakna Druhe vlakno : 8
Vystup vlakna Druhe vlakno : 9
Vystup vlakna Druhe vlakno : 10

Vystup vlakna Druhe vlakno : 100
Vystup vlakna Druhe vlakno : 101
Vystup vlakna Druhe vlakno : 102
Vystup vlakna Druhe vlakno : 103
Vystup vlakna Druhe vlakno : 104
Vystup vlakna Druhe vlakno : 105

Zdrojové kódy příkladů naleznete zde.

Následující díl seriálu bude opět pojednávat o práci s vlákny.

Diskuze (1) Další článek: Novinky z hardwaru: ATi Radeon X1800XT

Témata článku: , , , , , , , , , ,