2014-04-28 24 views
14

Sono un ingegnere software/hardware con una certa esperienza in C e tecnologie embedded. Attualmente sono impegnato a scrivere alcune applicazioni in C# (.NET) che utilizzano hardware per l'acquisizione dei dati. Ora il seguente, per me la masterizzazione, domanda:Polling nel modo giusto?

Ad esempio: Ho una macchina che ha un interruttore finale per rilevare la posizione finale di un asse. Ora sto usando un modulo di acquisizione dati USB per leggere i dati. Attualmente sto usando una discussione per leggere continuamente lo stato della porta.

Non ci sono funzionalità di interrupt su questo dispositivo.

La mia domanda: è questa la strada giusta? Dovrei usare timer, discussioni o attività? So che il sondaggio è qualcosa che molti di voi "odiano", ma ogni suggerimento è benvenuto!

+6

Perché dovremmo odiare il polling? –

+0

Esiste un evento come .OnFinalPositionArrived a cui è possibile connettersi? – Einer

+0

Dovresti usare un 'System.Threading.Timer' per eseguire il polling - che evita di creare un nuovo thread per farlo. –

risposta

33

IMO, questo dipende in gran parte dal tuo ambiente esatto, ma prima di tutto - Non dovresti usare Thread più nella maggior parte dei casi. Tasks sono la soluzione più comoda e più potente per questo.

  • a bassa frequenza di polling: Timer + polling in caso Tick:
    Un timer è facile da gestire e fermare. Non c'è bisogno di preoccuparsi di fili/compiti in esecuzione in background, ma la gestione avviene nel thread principale

  • media frequenza di polling: Task + await Task.Delay(delay):
    await Task.Delay(delay) non blocca un thread-pool di thread, ma a causa della cambio di contesto il ritardo minimo è ~ 15ms

  • alta frequenza di polling: Task + Thread.Sleep(delay)
    utilizzabile a 1ms ritardi - abbiamo effettivamente fare questo per interrogare il nostro dispositivo di misurazione USB

Questo potrebbe essere realizzato nel modo seguente:

int delay = 1; 
var cancellationTokenSource = new CancellationTokenSource(); 
var token = cancellationTokenSource.Token; 
var listener = Task.Factory.StartNew(() => 
{ 
    while (true) 
    { 
     // poll hardware 

     Thread.Sleep(delay); 
     if (token.IsCancellationRequested) 
      break; 
    } 

    // cleanup, e.g. close connection 
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); 

Nella maggior parte dei casi si può semplicemente utilizzare Task.Run(() => DoWork(), token), ma non c'è un sovraccarico di fornire l'opzione TaskCreationOptions.LongRunning che racconta la task-scheduler non utilizzare un normale filettatura filo della piscina
Ma come vedi Tasks sono più facili da gestire (e await in grado, ma non si applica qui). Soprattutto lo "stop" è solo chiamando cancellationTokenSource.Cancel() in questa implementazione da qualsiasi parte del codice.

È possibile condividere questo token in più azioni e fermarle contemporaneamente. Inoltre, le attività non ancora avviate non vengono avviate quando il token viene annullato.

È possibile anche allegare un'altra azione a un'attività a rincorrere un compito:

listener.ContinueWith(t => ShutDown(t)); 

Questo viene poi eseguito dopo l'ascoltatore completa e si può fare pulizia (t.Exception contiene l'eccezione dell'azione compiti se non ha avuto successo).

+0

L'ambiente è un tablet con Windows 8. L'applicazione è WPF. alto, quindi suggeriresti di usare un Task con Thread.Sleep (ritardo)? Perché i Thread non sono la soluzione giusta? Grazie per le informazioni! – Velocity

+0

Great! Grazie per il tuo esempio e spiegazione molto chiari! Non sapevo che il cambio di contesto del Task.Delay era di circa 15ms! Ancora la piccola domanda: perché i thread sono direttamente, non una buona soluzione in questo caso? A causa del pool di thread? – Velocity

+0

Vedere le mie estese spiegazioni, ma i thread non sono male se, ma la raccomandazione è di non usarli più (nella maggior parte dei casi) come il sovraccarico per i compiti sono minimi, ma i "vantaggi di usabilità" sono grandi ... – ChrFin

2

Il polling IMO non può essere evitato.

Quello che puoi fare è creare un modulo, con il suo thread indipendente/Task che eseguirà il polling della porta regolarmente.In base al cambiamento di dati, questo modulo aumenterà l'evento che verrà gestito dalle applicazioni che consumano

+0

Questa è anche una buona idea! Ma il modulo stesso è costituito da una discussione o da un'attività, giusto? E dopo il cambiamento spari un evento? – Velocity

+0

Scusa, l'hai già suggerito! Ho trascurato la tua risposta! ;) – Velocity

+0

Personalmente vorrei usare Task (http://blog.slaks.net/2013-10-11/threads-vs-tasks/), ma generalmente dipende dall'urgenza del lavoro e dal livello di confidenza con la Biblioteca –