by Logo Technical Blog – Future Processing
03.06.2013
Auto Load Dispatcher Timer_

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

Related Posts

Comments

Cookies

This website stores cookies on your computer. These cookies are used to improve our website and provide more personalized services to you, both on this website and through other media. To find out more about the cookies we use, see our Cookies policy.