Auto Load Dispatcher Timer

Auto Load Dispatcher Timer

data: 3 czerwca, 2013
czas czytania: 2 min
autor: Marcin Drobik

Bawiłem się ostatnio aplikacją (WPF, .NET 4.5, C# 5 async await), która wyświetla na żywo obraz z kamerki i skanuje go w poszukiwaniu pewnych informacji. Chcąc maksymalnie zwiększyć wydajność, doszedłem do implementacji Timera, który sam dostosowuje swój interwał, aby maksymalnie zrównoleglić periodyczne operacje i wykorzystać dostępne procesory.

Może nic skomplikowanego, ale jakoś nie potrafiłem sprawnie zgooglować dokładnie za tym, czego potrzebowałem (choć pewnie jest tego już pełno ;-)).

Wymagania

Moim podstawowym wymaganiem było, aby Timer obsługiwał operacje asynchroniczne tworzone za pomocą słów kluczowych async / await i aby sam dobierał swój interwał tak, by jak najwięcej operacji działo się równolegle bez wpływu na wątek UI – chciałem, żeby obraz z kamery wyświetlał się płynnie.

Implementacja

public class AutoLoadDispatcherTimer 
{

Klasa jest wrapperem wokół standardowego DispatcherTimera:

private readonly DispatcherTimer _internalTimer;

W konstruktorze, zamiast delegata z zewnątrz, podłączamy własny handler:

public AutoLoadDispatcherTimer() 
    { 
        _internalTimer = new DispatcherTimer(); 
        _internalTimer.Tick += AutoLoadCallback; 
    }

W handlerze dzieje się wszystko, co istotne. Przede wszystkim jest oznaczony słowem „async”:

private async void AutoLoadCallback(object sender, EventArgs eventArgs) 
    {

Za pomocą Stopwatcha mierzymy czas operacji. Operacje oczywiście wykonujemy asynchronicznie za pomocą słowa „await”:

_intervalStopwatch.Restart(); 
        await OnTick(); 
        _intervalStopwatch.Stop();

Na podstawie czasu i ilości dostępnych procesorów wyliczamy nowy interwał. Wyliczenie jest proste – jeśli mamy np. 3 dostępne rdzenie i zadanie zajmujące 1 sekundę, to jeśli będziemy uruchamiać kolejne iteracje zadania z opóźnieniem 333 ms, powinniśmy mieć zawsze jeden wolny rdzeń, gdy któreś zadanie się skończy.

  // calculate the interval, so the scanning will use all available cores
        var interval = (_intervalStopwatch.ElapsedMilliseconds / _availableProcessors); 
        _internalTimer.Interval = TimeSpan.FromMilliseconds(interval); 
    }

Ilość procesorów jest brana z klasy „Environment”. Domyślnie ustawienie zwraca ilość logicznych procesorów – 1, zostawiając jeden dla wątku UI.

private static int GetAvailableProcessors() 
{ 
    int availableProcessors = Environment.ProcessorCount - 1; 
    if (availableProcessors <= 0) 
        availableProcessors = 1; 

    return availableProcessors; 
}

W sumie to tyle – aplikacja, którą napisałem wyświetla na żywo obraz z WebKamerki i na moim kompie (4 logiczne procesory) potrafi osiągnąć 40 skanów na sekundę (czyli pewnie szybciej niż kamerka podaje obraz).

Poniżej załączam pełne źródła klasy:
AutoLoadDispatcherTimer.cs

Newsletter IT leaks

Dzielimy się inspiracjami i nowinkami z branży IT. Szanujemy Twój czas - obiecujemy nie spamować i wysyłać wiadomości raz na dwa miesiące.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.