reklama

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

Po seznámení se s pojmem synchronizace vláken a s tím související třídou System.Threading.Monitor bych v dnešním díle rád představil další možné využití této třídy.

Test na získání zámku objektu

V minulém díle, ve kterém jsme se seznámili s třídou Monitor a jejími metodami Enter a Exit, některé z vás možná napadlo, jestli je nějakým způsobem možné zjistit, zda-li je možné získat exklusivní zámek toho čí onoho objektu. Pokud bychom chtěli tuto informaci získat a v případě, že je zámek k dispozici, tento zámek použít k synchronizaci použijeme k tomu metodu TryEnter třídy Monitor.

/// <summary>
/// Ukazka pouziti metody TryEnter tridy Monitor
/// </summary>
internal class TryEnterExam
{
  //kolekce, ke ktere je vlakny pristupovano synchronizovane
  private ArrayList elements;
   
  internal TryEnterExam()
  {
    elements = new ArrayList();
  }

  private bool TryToAddElement(object Element)
  {
    //otestuje zda je mozne ziskat zamek objektu elements
    if (Monitor.TryEnter(elements))
    {
      //pokud se podari ziskat zamek pridame element
      elements.Add(Element);
      return true;
    }
    else
    {
      return false;
    }
  }
}

V příkladu se zkouší získat zámek pro objekt kolekce za účelem přidání dalšího prvku. Pokud se podaří daný zámek získat, je další objekt do kolekce přidán, v opačném případě se do kolekce nic nepřidá a je vrácena hodnota false, aby volající byl informován o tom, že se přidání prvku zdařilo.

V uvedeném zdrojovém kódu je použita nejjednodušší přetížená verze metody TryEnter , které je předán pouze objekt, pro který chceme zkusit zámek získat. Ovšem kromě této verze je nám k dispozici ještě jedna zajímavá verze této metody a ta kromě objektu přijímá ještě čas, po který má vlákno čekat na získání zámku. Takže pokud bychom řádek s voláním metody TryEnter změnili do této podoby, vlákno by zkusilo získat zámek objektu a pokud by se tak hned nepovedlo, počkalo by ještě jednu vteřinu, jestli nebude zámek uvolněn.

private bool TryToAddElement(object Element)
{
  /*otestuje zda je mozne ziskat zamek objektu elements,
   * pokud ne, bude se vterinu cekat, jestli nedojde k jeho
   * uvolneni*/
  if (Monitor.TryEnter(elements, 1000))
  {
    //pokud se podari ziskat zamek pridame element
    elements.Add(Element);
return true;
  }
  else
  {
    return false;
  }
}

Notifikace vláken a čekání na zámek

Zatím jsme se zabývali pouze tím, jak je možné získávat či uvolňovat zámky objektů a jejich uvolňováním, takže jsme v našich příkladech získali zámek, provedli požadované operace a po té zámek uvolnili. Podívejme se na následující zdrojový kód, který by již pro nás měl být lehce pochopitelný.

internal class NotPulsingExam
{
  internal static void Run()
  {
    Thread.CurrentThread.Name = "Prvni vlakno";
    Thread lSecond = new Thread(new ThreadStart(DoSomeWork));
    lSecond.Name = "Druhe vlakno";
    lSecond.Start();
    DoSomeWork();
  }
   
  internal static void DoSomeWork()
  {
    //ziskani zamku
    lock(typeof(NotPulsingExam))
    {
      for (int i = 0; i < 3; i++)
      {
        Console.WriteLine("{0} - {1}", Thread.CurrentThread.Name, i);
      }
    }
    //uvolneni zamku
  }
}

Předpokládejme, že bychom chtěli ,aby se tato dvě vlákna ve výpisu na systémovou konzoli střídala po vypsání jednotlivého znaku. K tomu abychom tohoto dosáhli, potřebujeme po vypsání jednotlivého znaku probudit další vlákno, které čeká na uvolnění zámku objektu a také je potřeba, aby vlákno, které již vypsalo znak, uvolnilo držený zámek. Jak na to se snaží ukázat tento příklad.

/// <summary>
/// Ukazkovy priklad na notifikaci vlaken, pomoci metody Pulse
/// tridy Monitor
/// </summary>
internal class PulsingExam
{
  internal static void Run()
  {
    Thread.CurrentThread.Name = "Prvni vlakno";
    PulsingExam lInstance = new PulsingExam();
    Thread lSecond = new Thread(new ThreadStart( lInstance.DoSomeWork));
    lSecond.Name = "Druhe vlakno";
    lSecond.Start();
    lInstance.DoSomeWork();
  }
   
  internal void DoSomeWork()
  {
    lock(this)
    {
      for(int i = 0; i < 3; i++)
      {
        Console.WriteLine("{0} - {1}", Thread.CurrentThread.Name, i);
        Monitor.Pulse(this);
        //zabraneni blokaci zamku (deadlocku)
        if (i < 2)
        {
          Monitor.Wait(this);
        }
      }
    }
  }
}

K tomu, abychom dosáhli výše zmíněného požadavku, použijeme metody Pulse a Wait třídy Monitor. Zavoláním metody Pulse totiž zařídíme, že je vzbuzeno další vlákno čekající na zámek objektu, jakmile jej aktuální vlákno uvolní. K tomu, aby aktuální vlákno uvolnilo držený zámek objektu slouží metoda Wait, která kromě operace uvolnění zámku zapříčiní i uspání aktuálního vlákna (bude čekat na opětovné uvolnění zámku vláknem jiným). Zámek držený aktuálním vláknem je nutné uvolnit, aby mohlo probuzené vlákno vstoupit do synchronizovaného bloku kódu.

Možná se podivujete nad podmínku kontrolující hodnotu proměnné i, která je použita v metodě DoSomeWork. K tomu, abychom mohli problém, který by mohl vzniknout, lépe pochopit se zkusme podívat na následující příklad.

/// <summary>
/// Ukazka mozneho zpusobeni blokace zamku metodou Pulse.
/// Spusteni tohoto prikladu zpusobi, ze program neskonci.
/// </summary>
internal class PulsingWithDeadlockExam
{
  internal static void Run()
  {
    Thread.CurrentThread.Name = "Prvni vlakno";
    PulsingWithDeadlockExam lInstance = new PulsingWithDeadlockExam();
    Thread lSecond = new Thread(new ThreadStart( lInstance.DoSomeWork));
    lSecond.Name = "Druhe vlakno";
    lSecond.Start();
    lInstance.DoSomeWork();
  }
   
  internal void DoSomeWork()
  {
    lock(this)
    {
      for (int i = 0; i < 3; i++)
      {
        Console.WriteLine("{0} - {1}", Thread.CurrentThread.Name, i);
        //probuzeni dalsiho vlakna
        Monitor.Pulse(this);
        //vzdani se zamku k objektu
        Monitor.Wait(this);
      }
      Console.WriteLine("Vlakno {0} skoncilo svou cinnost", Thread.CurrentThread.Name);
    }
  }
}

Pokud si zkusíte spustit tento příklad, zjistíte, že je běh programu, po vypsání čísel na systémovou konzoli, zablokován. Proč je tomu tak? Je to z důvodu, že při běhu tohoto příkladu dojde ke vzniku situace zvané deadlock (zablokované zámky) a to protože dojde k situaci, ve které jedno vlákno čeká na probuzení jiným vláknem, což se ale nikdy nestane, protože vlákno, které by mělo zavolat probuzení je již ukončeno.

Tomu je právě předcházeno použitím podmínky v předchozím příkladu, která zajistí, že pokud je prováděna poslední iterace práce vlákna, není již zavolána metoda Wait, aby nedošlo k nechtěnému zablokování (vlákno již nepotřebuje být znovu probuzeno, protože má svou práci hotovou a může skončit).

Dalším možným řešením tohoto problému je v určitých situacích použití přetížené verze metody Wait, které předáme čas, po který má čekat na probuzení jiným vláknem. Po vypršení tohoto času se totiž blokované vlákno přesune do aktivního stavu za účelem získaní zámku a pokračování v činnosti. A jelikož mu v našem příkladu v získání zámku již nebude jiné vlákno bránit (druhé vlákno již svou činnost skončilo), spuštění příkladu již neskončí zablokováním. Schválně si zkuste v příkladu ukazující vznik zablokování, změnit řádek s voláním metody Wait do této podoby.

Monitor.Wait(this, 1000);

Příklady související s článkem jsou k dispozici zde.

Témata článku: Software, Microsoft, Programování, Elements, Pulse, Element

3 komentáře

Nejnovější komentáře

  • Pavel Polívka 24. 11. 2007 13:46:29
    Programovou oflline verzi seriálu naleznete ke stažení na...
  • Lubos Hladik 14. 7. 2006 9:20:11
    Zdravim,
    nemelo by u prvnich dvou prikladu v tomto clanku pri splneni...
  • Silvius 2. 12. 2005 17:47:32
    A nestacilo by nodifikovat metodu DoSomeWork nasledovnym...
reklama
Určitě si přečtěte

Microsoft: Zbavte se už konečně zastaralých a děravých Windows 7

Microsoft: Zbavte se už konečně zastaralých a děravých Windows 7

** Microsoft pomalu začíná kritizovat svůj nejpopulárnější OS ** Chce konečně dostat podniky na Desítky ** Bezpečnostní podpora Sedmiček vydrží ještě necelé tři roky

17.  1.  2017 | Jakub Čížek | 403

Český státní blacklist už funguje. Ministerstvo financí se pochlubilo s detaily

Český státní blacklist už funguje. Ministerstvo financí se pochlubilo s detaily

** Dva týdny po Novém roce zajím zeje prázdnotou ** Ministerstvo vydalo metodický pokyn ** Takhle to bude fungovat v praxi

16.  1.  2017 | Jakub Čížek | 49

8 produktů, o kterých byste neřekli, že nesou značku Apple

8 produktů, o kterých byste neřekli, že nesou značku Apple

** Věděli jste, že Apple vyvinul celkem 45 modelů tiskáren? ** ** Monitor na výšku, plotter nebo herní konzole - to vše měl Apple ve své nabídce ** Většinu z těchto produktů pohřbil Steve Jobs

19.  1.  2017 | Stanislav Janů | 33

Opera Neon: Takto prý bude vypadat prohlížeč budoucnosti. Chcete to?

Opera Neon: Takto prý bude vypadat prohlížeč budoucnosti. Chcete to?

** Opera představila Neon ** Koncepci prohlížeče budoucnosti ** Připomíná Chrome OS

16.  1.  2017 | Jakub Čížek | 35


Aktuální číslo časopisu Computer

99 nejlepších programů pro váš počítač

Zvykejte si na umělou inteligenci

Velké testy PC zdrojů a gamepadů

Alternativní zdroje energie

reklama
reklama