Artykuł

freeimages.com freeimages.com
paź 02 2016
0

Obsługa wielu tasków na raz w C#

W dobie wielordzeniowych procesorów, umiejętność programowania współbieżnego jest niezwykle istotna. Scenariusz programowania synchronicznego, w którym wszystko wkładamy w jeden wątek i kolejkujemy może spowodować, że nasza aplikacja będzie funkcjonować gorzej od konkurencji. Dobrym tego przykładem są aplikacje operujące na żądaniach sieciowych.

Przykładowo aplikacja mobilna może na starcie wymagać pobrania konfiguracji, ściągnięcia informacji na ekran typu home, czy też aktywacji opcji auto-logowania na bazie zapisanych wcześniej danych użytkownika.

Wszystkie te operacje możemy wykonać w trybie synchronicznym, w którym to będziemy wykonywać każdą operację po kolei. Dużo lepszą opcją będzie jednak w tym przypadku programowanie asynchroniczne zrealizowane za pomocą tasków. Taski mogą być wykonywane w tym samym czasie i jednocześnie wciąż możemy dojść do punktu, w którym będziemy oczekiwać na rezultat działania wszystkich uruchomionych zadań. Poniżej krótki przepis na sukces w nowoczesnym programowaniu współbieżnym.

Task.WhenAll

Programowanie współbieżne zostało mocno uproszczone w momencie gdy w .Net Framework 4 pojawiła się klasa Task, a także towarzyszące jej słowa kluczowe async/await. Jednocześnie zapanował szał, by przerobić wszystkie istniejące interfejsy na te asynchroniczne, co doprowadzało m.in. do tworzenia rozwiązań typu async, w miejscach gdzie ich nie powinno być - casus Json.Net.

Na szczęście istnieje sporo rozwiązań, które zostały rzeczywiście napisane w sensowny sposób i z których możemy korzystać we własnych aplikacjach.

Sporym ich plusem jest również fakt, że nawet jeśli startujemy z jednego punktu, to możemy rozpocząć kilka operacji asynchronicznych, które będą wykonywane równolegle, a następnie zaczekać na ich rezultat w tym samym miejscu. W ten sposób możemy choćby wykonywać kilka żądań do API jednocześnie, co w wymierny sposób skróci czas przetwarzania - zwłaszcza jeśli backend serwera może odpowiadać przez dłuższy czas. Aby osiągnąć taki efekt, wystarczy skorzystać z metody Task.WhenAll. Poniżej krótki przykład:

public class App
{
    public async Task Run()
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        await Task.WhenAll(TaskA(), TaskB(), TaskC());
        watch.Stop();
        Console.WriteLine($"Execution time: {watch.ElapsedMilliseconds}ms.)");
    }

    private async Task TaskA()
    {
        await Task.Delay(1500);
        Console.WriteLine("TaskA executed");
    }

    private async Task TaskB()
    {
        await Task.Delay(1000);
        Console.WriteLine("TaskB executed");
    }

    private async Task TaskC()
    {
        await Task.Delay(2000);
        Console.WriteLine("TaskC executed");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        App app = new App();
        Task.Run(async () => {
            await app.Run();
        }).Wait();
    }
}

Warto zwrócić uwagę tutaj na kilka faktów:

  • Uruchomiliśmy trzy taski jednocześnie
  • Każdy z nich miał inną długość czasu przetwarzania
  • Wszystkie taski zakończyły się po 2 sekundach, w momencie zakończenia najdłuższego tasku
  • Metoda Task.WhenAll, działa świetnie:)

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus