Seriály Reklama
Vývojářské akce Spřízněné servery

Novinky v C# 4 a .NET Frameworku 4.0: Další vylepšení pro multivláknové programování

Jiří Činčura | Vydáno 31. srpna 2009 | Nový komentář | .NET

Minulý díl byl zaměřen především na novinky pro podporu fine-grained paralelizmu. Avšak někdy je třeba použít stará dobrá vlákna a ponořit se do vlastní multivláknové výzvy. Ani pro klasická vlákna nezůstává .NET Framework 4 pozadu.

reklama

Minulý díl byl zaměřen především na novinky pro podporu fine-grained paralelizmu. Avšak někdy je třeba použít stará dobrá vlákna a ponořit se do vlastní multivláknové výzvy. Ani pro klasická vlákna nezůstává .NET Framework 4 pozadu.

Jednou z pravděpodobně nejcitelněji chybějících věcí v .NET 2.0 byla chybějící podpora pro thread-safe generické kolekce. V .NETu 1.0/1.1 bylo možné využít metody Synchronized a obléci tak kolekci do thread-safe kabátu. Na výběr tedy zbyly dvě možnosti. Používat negenerické verze a Synchronized nebo si napsat kolekci vlastní (případně využít hotových knihoven).

.NET Framework 4 přináší nově mnoho thread-safe kolekcí. Nachází se ve jmenném prostoru Systém.Collections.Concurrent. Jedná se např. o ConcurrentDictionary<TKey, TValue>, ConcurrentQueue<T>, ConcurrentStack<T> a jiné. Výběr je opravdu bohatý. My se podíváme na použití ConcurrentQueue<T>.

ConcurrentQueue<int> fronta = new ConcurrentQueue<int>();
fronta.Enqueue(20);
int prvek;
if (fronta.TryDequeue(out prvek))
{
	Console.WriteLine(prvek);
}
else
{
	Console.WriteLine("Nic");
}

Výše je uveden jednoduchý příklad. Nejprve vytvoříme novou thread-safe frontu – konstruktor se neliší od konstruktoru běžné fronty. Stejně tak metoda Enqueue, která přidá prvek do fronty. Co vás však může překvapit je absence komplementární metody Dequeue. Skutečně není obsažena, neboť její použití v konkurenčním prostředí není v mnoha případech dobrý nápad. Většinou je třeba nejprve zjistit, zdali fronta vůbec nějaký prvek obsahuje a až poté jej získat. Ale mezi tímto zjištěním a vlastním získáním může jiné vlákno frontu pozměnit a výsledkem bude špatné chování. Proto existuje metoda TryDequeue (případně TryPop u zásobníku apod.), která vám vrátí požadovaný prvek a ještě informaci, jestli operace byla úspěšná.

Použití těchto datových struktur je velmi přímočaré, a pokud mezi vlákny kolekce využíváte, jejich záměna nebude jistě nijak obtížná.

Mimo kolekcí .NET Framework 4 přináší i nová synchronizační primitiva a podobné objekty, které mohou opravdu naši práci rapidně ulehčit.

Pokud je vaše kritická sekce v aplikaci velice krátká a čekání nebude dlouhé a víte (nebo tušíte), že přepnutí kontextu a přechody do režimu jádra by bylo náročnější než busy-waiting, pravděpodobně se poohlédnete po objektu ne nepodobnému objektu SpinWait resp. SpinLock. Tyto dva objekty jsou nové a slouží přesně k vyřešení situace, kterou jsem právě popsal. Jejich nasazení je však třeba velmi pečlivě vyzkoušet, neboť při nesprávném použití se aplikace může stát téměř nepoužitelnou.

S čekáním na jistou událost se nepřímo pojí i čekání na dokončení práce vláken (např. v ThreadPoolu). Jeden z nových objektů i CountdownEvent. Tento objekt dělá vlastně jen věc, kterou jste již minimálně jednou psali. Počítá, kolikrát někdo signalizoval událost a jakmile dojde k nule (od startovní hodnoty), vyvolá sám událost.

class Program
{
    static void Main(string[] args)
    {
        CountdownEvent cde = new CountdownEvent(10);
        for (int i = 0; i < cde.InitialCount; i++)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(Dummy), cde);
        }
        cde.Wait(2000);
        Console.WriteLine("Za 2 sekundy hotovo: {0}.", cde.InitialCount - cde.CurrentCount);
        cde.Wait();
        Console.WriteLine("Vše hotovo.");
    }

    static void Dummy(object o)
    {
        Thread.Sleep(new Random().Next(5000));
        (o as CountdownEvent).Signal();
    }
}

Nakonec se ještě zmíním o několika menších „zlepšovácích“, které považuji za zajímavé.

Jedním z nich je Lazy<T> (a LazyInitializer) umožňující „líně“ inicializovat proměnnou. Objekt je to v zásadě jednoduchý, ale ve spojení s LazyExecutionMode vám může ušetřit psaní několika zbytečných řádků kódu. Podobně je možné využít i ThreadLocal<T> objekt, který můžete použít pro proměnné pro dané vlákno.

Posledním užitečným objektem je BlockingCollection<T>. Využití této kolekce je primárně pro klasickou synchronizační úlohu producent-konzument. Avšak není problém využít i ConcurrentQueue<T> nebo ConcurrentStack<T>. Tato kolekce pouze ulehčuje přímý scénář producent-konzument, který se velmi často při vícevláknovém zpracování v různých variacích objevuje.

Ačkoli jsme prošli jen malou část novinek ve vícevláknovém světě v .NET Frameworku 4, považuji jej za velmi podařené a užitečné. Pokud vás detaily zajímají více, doporučuji navštívit msdn.microsoft.com a zkoušet, zkoušet, zkoušet.

V režimu TEXT nejsou povoleny žádné HTML značky. Odstavce jsou vytvořeny automaticky, webové adresy a e-maily jsou převedeny na odkazy. V režimu "HTML" jsou povoleny následující elementy: a b i cite strong em p br code blockquote ul ol li. Redakce si vyhrazuje právo komentáře mazat.








Uživatel

Pro zobrazení informací o svém účtu se musíte přihlásit. Pokud ještě nemáte svůj účet, tak si ho prosím vytvořte!

Reklama
Seriál Odběr článků

Pokud se přihlásíte k odběru zpráv, pošle Vám systém každý týden e-mail se seznamem posledních článků.

Zdrojak .NET Hosting Reklama
 
  Copyright 2002-2007 Devmasters s.r.o., Michal Bláha a autoři | running on Nemesis Publishing | Právní doložka  
TOPlist