We can do asynchronous programming in three different ways: Wait-Unitl-Done which allows you start asynchronous call and do other work, Polling which polls IAsyncResult and see if it is finished by calling on IsCompleted method of IAsyncResult, Call Back where we specify any method we need to call back on and we need to include a state of this call back.
Asynchronous programming has following intricacy of exception handling. It does not happen during the asynchronous process but rather during EndXXX call and we need to keep this in mind when trying to handle exceptions within our code.
Now we know quite a bit about Asynchronous programming principles and we can even implement one ourselves but this is not really needed with .NET Framework and C#. The main reason is that .NET Framework already has built in thread pool.
WaitCallback workItem = new WaitCallback(WorkWithParameter));
if (!ThreadPool.QueueUserWorkItem(workItem, "ThreadPooled"))
Console.WriteLine("Could not queue item");
Thread pooling is very useful in many ways but first and foremost it helps to reduce processing power of the system by reusing threads instead of creating new once when we need one.
We use WaitCallback when adding to a thread pool and we use AsyncCallback when creating threads manually via BeginInvoke, EndEnvoke
Method of ThreadPool
|GetAvailableThreads||Gets available threads|
|GetMaxThreads||Gets the maximum number of threads|
|GetMinThreads||Gets the minimum number of threads|
|QueueUserWorkItem||Adds a piece of work to the thread pool|
|RegisterWaitForSingleObject||Makes a callback to be issued|
|SetMaxThreads||Sets the max. number of threads|
|SetMinThreads||Sets the min. number of threads|
|UnsafeQueueNativeOverlapped||Used to queue asynchronous File I/O|
|UnsafeQueueUserWorkItem||Queues a work item for a thread for high-performance|
|UnsafeRegisterWaitForSingleObject||Makes a callback for a specific WaitHandle|
We may want increase or decrease number of threads available. The two main reasons are: thread starvation and start-up cost. If we need to increase threads because we have too many processes we need to call on ThreadPool.SetMaxThreads or ThreadPool.SetMinThreads if we need to reduce amount of threads.
ThreadPool.GetMaxThreads(out threads, out myCompletionPorts); // Get
ThreadPool.SetMaxThreads(threads + 10, myCompletionPorts + 100); // Set
ThreadPool.GetMinThreads(out threads, out myCompletionPorts); // Get
ThreadPool.SetMinThreads(threads + 10, myCompletionPorts + 100); // Set
ThreadPool can also work with kernel-level synchronization objects such as Mutex, Semaphore and Event by calling on ThreadPool.RegisterWaitHandle.
In addition, Asynchronous coding varies between Windows Forms and ASP.NET in a way we do implementation. In order to abstract our development from these two forms we employ SynchronizationContext Class. It is also worthwhile to mention Timer object that will trigger asynchronous call to a method based on time we need to execute such a method. We employ delegate to call on the method we need to execute. This is used basically when we need to make reoccurring calls over time.
Timer mytm = new Timer(new TimerCallback(TimerTick), null, 0, 1000);
// Using Infinite
mytm.Change(Timeout.Infinite, 1000); //update timer.
static void MyTimerTick(object state)