How much do I need to worry about blocking tasks in .NET? i.e. how does the .NET task scheduler handle blocking of threads in the thread pool and oversubscription?
E.g. if I have some IO in a task, should I always create it with the LongRunning
hint? Or does the task scheduler heuristics handle it better? In C++ there is an Oversubscribe
hint which works perfectly but I have not found any equivalent in .NET.
You do need to worry about it if you want the most performant code.
The best way to handle it is to use the .Net 4.5 "await" style of I/O.
If you don't have .Net 4.5, you will have to use the older style of I/O (which works just as well, but is harder to use).
The non-blocking I/O described in those articles is by far the best way to do your I/O with multiple threads.
If you are not using I/O then you might still learn much from those articles.
The ThreadPool does detect when one of its threads blocks and it is a hint for it to add another thread to the pool. So, if you block a lot, the performance most likely won't be terrible, because ThreadPool will try to keep your CPU cores busy.
But having many blocked threads can be a performance problem, because it increases memory consumption and can lead to more context switches.
Also, this behavior may lead to decreased performance of IO. With spinning disks (HDDs), accessing many files at the same time causes lots of seeking, which can affect the performance drastically.
LongRunning
signals to the TPL not to use a threadpool thread--it creates a non-threadpool thread to fulfill the request (e.g.new Thread(...)
). this is not what you should be doing for IO. You should be using asynchronous IO. For example:this ensures, wherever possible, overlapped IO is used--which uses the IO threadpool.
If you want to use a Task with a legacy APM API, you can use FromAsync:
If you need to deal with a legacy event async API, you can use TaskCompletionSource: