It is quite interesting to see how many people don’t understand the basics of threading, while it is a difficult topic (especially to debug), and it is not the silver bullet for all the problems, but it sure is quite handy for certain things. With the inevitable shift to multi-core CPU’s and the release of Vista to take better opportunity of these extra power, more applications in the future should be ready to exploit them. Which makes it quite important for the average developer to start playing and understanding this.
If you are new to this, one of the classes I would recommend reading up on is called ExecutionContext class. This class essentially is the equivalent of the COM Apartment (from the good old COM days). This class provides a single container for all information relevant to a logical thread of execution which includes security context, call context, synchronization context, localization context, and transaction context. You cannot change context of the thread (to which a ExecutionContext is attached), you can only copy it to another thread. If you do try and copy it you will get an Exception.
Here is an example from MSDN on how to use this (also testing the new CopyAsHTML Addin):
1 using System;
2 using System.Threading;
3 using System.Security;
4 using System.Collections;
5 using System.Security.Permissions;
6 using System.Runtime.Serialization;
7 using System.Runtime.Remoting.Messaging;
8
9 namespace Contoso
10 {
11 class ExecutionContextSample
12 {
13 static void Main()
14 {
15 try
16 {
17 Console.WriteLine(“Executing Main in the primary thread.”);
18 FileDialogPermission fdp = new FileDialogPermission(
19 FileDialogPermissionAccess.OpenSave);
20 fdp.Deny();
21 // Capture the execution context containing the Deny.
22 ExecutionContext eC = ExecutionContext.Capture();
23
24 // Suppress the flow of the execution context.
25 AsyncFlowControl aFC = ExecutionContext.SuppressFlow();
26 Thread t1 = new Thread(new ThreadStart(DemandPermission));
27 t1.Start();
28 t1.Join();
29 Console.WriteLine(“Is the flow suppressed? “ +
30 ExecutionContext.IsFlowSuppressed());
31 Console.WriteLine(“Restore the flow.”);
32 aFC.Undo();
33 Console.WriteLine(“Is the flow suppressed? “ +
34 ExecutionContext.IsFlowSuppressed());
35 Thread t2 = new Thread(new ThreadStart(DemandPermission));
36 t2.Start();
37 t2.Join();
38 // Remove the Deny.
39 CodeAccessPermission.RevertDeny();
40 // Capture the context that does not contain the Deny.
41 ExecutionContext eC2 = ExecutionContext.Capture();
42 // Show that the Deny is no longer present.
43 Thread t3 = new Thread(new ThreadStart(DemandPermission));
44 t3.Start();
45 t3.Join();
46
47 // Set the context that contains the Deny.
48 // Show the deny is again active.
49 Thread t4 = new Thread(new ThreadStart(DemandPermission));
50 t4.Start();
51 t4.Join();
52 // Demonstrate the execution context methods.
53 ExecutionContextMethods();
54 Console.WriteLine(“Demo is complete, press Enter to exit.”);
55 Console.Read();
56 }
57 catch (Exception e)
58 {
59 Console.WriteLine(e.Message);
60 }
61 }
62 // Execute the Demand.
63 static void DemandPermission()
64 {
65 try
66 {
67 Console.WriteLine(“In the thread executing a Demand for “ +
68 “FileDialogPermission.”);
69 new FileDialogPermission(
70 FileDialogPermissionAccess.OpenSave).Demand();
71 Console.WriteLine(“Successfully demanded “ +
72 “FileDialogPermission.”);
73 }
74 catch (Exception e)
75 {
76 Console.WriteLine(e.Message);
77 }
78 }
79
80 static void ExecutionContextMethods()
81 {
82 // Generate a call context for this thread.
83 ContextBoundType cBT = new ContextBoundType();
84 cBT.GetServerTime();
85 ExecutionContext eC1 = ExecutionContext.Capture();
86 ExecutionContext eC2 = eC1.CreateCopy();
87 Console.WriteLine(“The hash code for the first execution “ +
88 “context is: “ + eC1.GetHashCode());
89
90 // Create a SerializationInfo object to be used for getting the
91 // object data.
92 SerializationInfo sI = new SerializationInfo(
93 typeof(ExecutionContext),
94 new FormatterConverter());
95
96 eC1.GetObjectData(
97 sI,
98 new StreamingContext(StreamingContextStates.All));
99
100 LogicalCallContext lCC = (LogicalCallContext)sI.GetValue(
101 “LogicalCallContext”,
102 typeof(LogicalCallContext));
103
104 // The logical call context object should contain the previously
105 // created call context.
106 Console.WriteLine(“Is the logical call context information “ +
107 “available? “ + lCC.HasInfo);
108 }
109 }
110
111
112 // One means of communicating between client and server is to use the
113 // CallContext class. Calling CallContext effectivel puts the data in a thread
114 // local store. This means that the information is available to that thread
115 // or that logical thread (across application domains) only.
116 [Serializable]
117 public class CallContextString : ILogicalThreadAffinative
118 {
119 String _str = “”;
120
121 public CallContextString(String str)
122 {
123 _str = str;
124 Console.WriteLine(“A CallContextString has been created.”);
125 }
126
127 public override String ToString()
128 {
129 return _str;
130 }
131 }
132
133 public class ContextBoundType : ContextBoundObject
134 {
135 private DateTime starttime;
136
137 public ContextBoundType()
138 {
139 Console.WriteLine(“An instance of ContextBoundType has been “ +
140 “created.”);
141 starttime = DateTime.Now;
142 }
143 [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
144 public DateTime GetServerTime()
145 {
146 Console.WriteLine(“The time requested by a client.”);
147 // This call overwrites the client’s
148 // CallContextString.
149 CallContext.SetData(
150 “ServerThreadData”,
151 new CallContextString(“This is the server side replacement “ +
152 “string.”));
153 return DateTime.Now;
154 }
155 }
156
157 }