Polling the right way? Polling the right way? multithreading multithreading

Polling the right way?


IMO, this heavily depends on your exact environment, but first off - You should not use Threads anymore in most cases. Tasks are the more convenient and more powerful solution for that.

  • Low polling frequency: Timer + polling in the Tick event:
    A timer is easy to handle and stop. No need to worry about threads/tasks running in the background, but the handling happens in the main thread

  • Medium polling frequency: Task + await Task.Delay(delay):
    await Task.Delay(delay) does not block a thread-pool thread, but because of the context switching the minimum delay is ~15ms

  • High polling frequency: Task + Thread.Sleep(delay)
    usable at 1ms delays - we actually do this to poll our USB measurement device

This could be implemented as follows:

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);

In most cases you can just use Task.Run(() => DoWork(), token), but there is no overload to supply the TaskCreationOptions.LongRunning option which tells the task-scheduler to not use a normal thread-pool thread.
But as you see Tasks are easier to handle (and awaitable, but does not apply here). Especially the "stopping" is just calling cancellationTokenSource.Cancel() in this implementation from anywhere in the code.

You can even share this token in multiple actions and stop them at once. Also, not yet started tasks are not started when the token is cancelled.

You can also attach another action to a task to run after one task:

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

This is then executed after the listener completes and you can do cleanup (t.Exception contains the exception of the tasks action if it was not successful).


IMO polling cannot be avoided.

What you can do is create a module, with its independent thread/Task that will poll the port regularly. Based on the change in data, this module will raise the event which will be handled by the consuming applications


May be:

   public async Task Poll(Func<bool> condition, TimeSpan timeout, string message = null)    {        // https://github.com/dotnet/corefx/blob/3b24c535852d19274362ad3dbc75e932b7d41766/src/Common/src/CoreLib/System/Threading/ReaderWriterLockSlim.cs#L233         var timeoutTracker = new TimeoutTracker(timeout);        while (!condition())        {            await Task.Yield();            if (timeoutTracker.IsExpired)            {                if (message != null) throw new TimeoutException(message);                else throw new TimeoutException();            }        }    }

Look into SpinWait or into Task.Delay internals either.