Free mag vol1 | Page 754

CHAPTER 19  MULTITHREADED, PARALLEL, AND ASYNC PROGRAMMING reading bogus data, which is sure to give way to extremely odd (and very hard to find) bugs, which are even harder to replicate and debug. Atomic operations, on the other hand, are always safe in a multithreaded environment. Sadly, there are very few operations in the .NET base class libraries that are guaranteed to be atomic. Even the act of assigning a value to a member variable is not atomic! Unless the .NET Framework 4.5 SDK documentation specifically says an operation is atomic, you must assume it is thread-volatile and take precautions. The Role of Thread Synchronization At this point, it should be clear that multithreaded programs are in themselves quite volatile, as numerous threads can operate on the shared resources at (more or less) the same time. To protect an application’s resources from possible corruption, .NET developers must make use of any number of threading primitives (such as locks, monitors, and the [Synchronization] attribute or language keyword support) to control access among the executing threads. Although the .NET platform cannot make the difficulties of building robust multithreaded applications completely disappear, the process has been simplified considerably. Using types defined within the System.Threading namespace, the .NET 4.0 and higher Task Parallel Library (TPL), and the .NET 4.5 C# async and await language keywords, you are able to work with multiple threads with minimal fuss and bother. Before diving into the System.Threading namespace, the TPL, and the C# async and await keywords, we will begin by examining how the .NET delegate type can be used to invoke a method in an asynchronous manner. While it is most certainly true that under .NET 4.5, the new C# async and await keywords offer a simpler alternative to asynchronous delegates, it is still important that you know how to interact with code using this approach (trust me; there is a ton of code in production that makes use of asynchronous delegates). A Brief Review of the .NET Delegate Recall that a .NET delegate is essentially a type-safe, object-oriented, function pointer. When you define a .NET delegate type, the C# compiler responds by building a sealed class that derives from System.MulticastDelegate (which in turn derives from System.Delegate). These base classes provide every delegate with the ability to maintain a list of method addresses, all of which may be invoked at a later time. Consider the following BinaryOp delegate, first defined in Chapter 10: // A C# delegate type. public delegate int BinaryOp(int x, int y); Based on its definition, BinaryOp can point to any method taking two integers (by value) as arguments and returning an integer. Once compiled, the defining assembly now contains a full-blown class definition that is dynamically generated when you build your project, based on the delegate declaration. In the case of BinaryOp, this class looks more or less like the following (shown in pseudocode): public sealed class BinaryOp : System.MulticastDelegate { public BinaryOp(object target, uint functionAddress); public int Invoke(int x, int y); public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state); public int EndInvoke(IAsyncResult result); } 699