2017-01-13 62 views
5

Jest to coś, z czego zawsze korzystałem Threads/BackgroundWorker, ale próbuję przenieść się do trybu Task w celu robienia rzeczy.Ciągłe pobieranie z użyciem zadań

Załóżmy, że mam zestaw SDK innej firmy, którego używam do czytania bajtów z portu USB. To wywołanie odczytu jest blokowane i kończy się po 100 ms, jeśli nie zostaną przeczytane żadne bajty, zwracając wartość null. Zwraca natychmiast, jeśli bajty są odczytywane, zwracając tablicę bajtów [] przeczytanych bajtów.

Tak więc w zasadzie muszę ciągle sprawdzać sondowanie i podejmować działania na odebranych bajtach, wywołując funkcję Parsing. Jest to aplikacja WPF, więc zwrócone bajty powinny być przekazywane do funkcji wątku interfejsu użytkownika.

Jakie jest właściwe podejście do robienia tego? To, co mam tak daleko, i wydaje się, aby pracować, ale chcę, aby upewnić się, że jest to właściwy sposób robienia rzeczy używając OC:

private void _connectUsbButton_Click(object sender, RoutedEventArgs e) 
{ 
    ListenForUsbMessagesAsync(); 
} 

private async void ListenForUsbMessagesAsync() 
{ 
    while (true) 
    { 
     byte[] readBytes = await ReadBytesAsync(); 
     Parse(readBytes); 
    } 
} 

private Task<byte[]> ReadBytesAsync() 
{ 
    Task<byte[]> readBytesTask = Task.Run(() => 
    { 
     byte[] bytes; 

     do 
     { 
      bytes = ReadBytes(); 
     } while (bytes == null); 

     return bytes; 
    }); 

    return readBytesTask; 
} 

private byte[] ReadBytes() 
{ 
    byte[] readBytes = _usbSdk.ReadBytes(); //100ms timeout (returns null if no bytes read) 
    return readBytes; 
} 
+0

'' Task jest dla ostatecznego wyniku, który nie zdarza się okresowo. –

+0

@ DanielA.White dlatego ciągle wywołuję go w pętli while (true). Ostateczny wynik to odczytanie niektórych bajtów. Bajty nie są odczytywane cały czas, w rzeczywistości przez większość czasu nie są czytane żadne bajty. – Eternal21

+0

dlaczego nie lubisz "Nici" do tego rodzaju zadań? 'Zadania' są zaprojektowane tak, aby szybko zakończyć, podczas gdy twoje IO może zająć minuty – slawekwin

Odpowiedz

4

wygląda ok do mnie, zaledwie kilka sugestii tutaj:

private async Task ListenForUsbMessagesAsync(CancellationToken token) 
{ 
    while (true) 
    { 
     byte[] readBytes = await ReadBytesAsync(); 
     Parse(readBytes); 
     token.ThrowIfCancellationRequested(); 
    } 
} 

gdzieś indziej, jak w WPF Okno .ctor przechowywać ten

var tokenSource = new System.Threading.CancellationTokenSource(); 

końcu zadzwonić do funkcji tak

private void _connectUsbButton_Click(object sender, RoutedEventArgs e) 
{ 
    ListenForUsbMessagesAsync(tokenSource.Token); 
} 

ten sposób można anulować zadania w dowolnym momencie poprzez wywołanie

tokenSource.Cancel() 

Ewentualnie, jeśli nie chcesz używać Zadania można tarło nowy wątek i przekazać w obiekcie Dispatcher. W ten sposób nowo utworzony wątek może bezpiecznie wyrzucać elementy na wątek UI.

5

Ponieważ zadanie odpytywania może działać przez długi czas, należy pomyśleć o uruchomieniu go w dedykowanym wątku. Można to osiągnąć, przekazując flagę TaskCreationOptions.LongRunning podczas tworzenia zadania odpytywania.

tak:

Task<byte[]> readBytesTask = Task.Factory.StartNew(() => 
    { 
     byte[] bytes; 

     do 
     { 
      bytes = ReadBytes(); 
     } while (bytes == null); 

     return bytes; 
    }, TaskCreationOptions.LongRunning); 
+0

Próbowałem dodawać twoje sugestie, ale kończy się to błędem kompilatora w linii 'return bytes': CS0029 \t Nie można niejawnie przekonwertować typu 'byte []' na 'System.Threading.Tasks.Task' – Eternal21

+1

naprawił to, potrzebujesz używać Task.Factory, aby móc przekazać TaskCreationOptions (zapomnij o tym przeciążeniu jest dostępna tylko w Factory) – barakcaf