9780735627048f Good morning everyone, Jeffrey Richter here. Today I thought I’d share a section of my new book, CLR via C#, Third Edition, with you. It’s from Chapter 26, “Compute-Bound Asynchronous Operations.” The section discusses how to flow contextual information from one thread to another thread by using the CLR’s Execution Context infrastructure. During the discussion, I go into the security and performance details of this feature as well.

Execution Contexts
Every thread has an execution context data structure associated with it. The execution
context includes things such as security settings (compressed stack, Thread’s
Principal property, and Windows identity), host settings (see System.Threading.
HostExecutionContextManager
), and logical call context data (see System.Runtime.
Remoting.Messaging.CallContext’s LogicalSetData
and LogicalGetData methods).
When a thread executes code, some operations are affected by the values of the thread’s
execution context settings. This is certainly true for the security settings. Ideally, whenever a
thread uses another (helper) thread to perform tasks, the issuing thread’s execution context
should flow (be copied) to the helper thread. This ensures that any operations performed
by helper thread(s) are executing with the same security settings and host settings. It also
ensures that any data stored in the initiating thread’s logical call context is available to the
helper thread.

By default, the CLR automatically causes the initiating thread’s execution context to flow to
any helper threads. This transfers context information to the helper thread, but it comes at a
performance cost because there is a lot of information in an execution context, and accumulating
all of this information and then copying it for the helper thread takes a fair amount of
time. If the helper thread then employs additional helper threads, then more execution context
data structures have to be created and initialized as well.

In the System.Threading namespace, there is an ExecutionContext class that allows you to
control how a thread’s execution context flows from one thread to another. Here is what the
class looks like:

public sealed class ExecutionContext : IDisposable, ISerializable {
    [SecurityCritical] public static AsyncFlowControl SuppressFlow();
    public static void RestoreFlow();
    public static Boolean IsFlowSuppressed();

    // Less commonly used methods are not shown
}

You can use this class to suppress the flowing of an execution context, thereby improving
your application’s performance. The performance gains can be quite substantial for a
server application. There is not much performance benefit for a client application, and the
SuppressFlow method is marked with the [SecurityCritical] attribute, making it impossible
to call in some client applications (like Silverlight). Of course, you should suppress the
flowing of execution context only if the helper thread does not need or access the context
information. If the initiating thread’s execution context does not flow to a helper thread, the
helper thread will use whatever execution context it last associated with it. Therefore, the
helper thread really shouldn’t execute any code that relies on the execution context state
(such as a user’s Windows identity).

Here is an example showing how suppressing the flow of execution context affects data in a
thread’s logical call context when queueing a work item to the CLR’s thread pool1:

public static void Main() {
    // Put some data into the Main thread's logical call context
    CallContext.LogicalSetData("Name", "Jeffrey");

    // Initiate some work to be done by a thread pool thread
    // The thread pool thread can access the logical call context data
    ThreadPool.QueueUserWorkItem(
    state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));

    // Now, suppress the flowing of the Main thread's execution context
    ExecutionContext.SuppressFlow();

    // Initiate some work to be done by a thread pool thread
    // The thread pool thread can NOT access the logical call context data
    ThreadPool.QueueUserWorkItem(
    state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name")));

    // Restore the flowing of the Main thread's execution context in case
    // it employs more thread pool threads in the future
    ExecutionContext.RestoreFlow();
    ...
}

When I compile and run the code above, I get the following output:

Name=Jeffrey
Name=

While this discussion has focused on suppressing the flow of execution context when calling
ThreadPool.QueueUserWorkItem, this technique is also useful when using Task objects (discussed
in the “Tasks” section of this chapter) and is also useful when initiating asynchronous
I/O operations (discussed in Chapter 27, “I/O-Bound Asynchronous Operations”).

 

Devon here.  Jeffrey’s book will be available via online retailers around February 15. The book’s ISBN is 978-0735627048, and it contains 704 pages.