-
Notifications
You must be signed in to change notification settings - Fork 59
Description
If DogStatsdService is configured from a task that uses a non-default task scheduler, AsynchronousWorker uses that task scheduler for its worker tasks. This can result in worker tasks blocking threads the application uses, such as the application's main thread. AsynchronousWorker should explicitly use TaskScheduler.Default instead of inheriting the current task's scheduler.
Minimal code to reproduce this in a Windows Forms application. This schedules a short-lived task on the main thread for configuring DogStatsdService. A long-lived worker task ends up on the main thread, causing the application to freeze:
public Form1()
{
// Initialise DogStatsd on the main thread _after_ the constructor returns.
Task.Factory.StartNew( () => {
dogStatsdSvc = new DogStatsdService();
dogStatsdSvc.Configure( new StatsdConfig()
{
StatsdServerName = "localhost",
StatsdPort = 8125
} );
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext() );
}The code that creates the worker tasks in AsynchronousWorker (line 39):
_workers.Add(Task.Factory.StartNew(() => Dequeue(), TaskCreationOptions.LongRunning));I believe this should be changed to:
_workers.Add(Task.Factory.StartNew(() => Dequeue(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default));Using Task.Run would be even better if you don't need compatibility with .NET versions before 4.5.
For more information on the issues of using Task.Factory.StartNew without specifying a task scheduler, see https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html. Search for and read from "Confusing default scheduler".