One of the simplest ways to consider threading is to limit object instance access to the thread that created the instance. This is how the Thread Affine threading model works.
Adding the Thread Affine model to a class
To apply the Thread-Affine threading model to a class:
Add the
PostSharp.Patterns.Threadingpackage to your project.Import the
PostSharp.Patterns.Threadingnamespace into your file.Add the ThreadAffineAttribute to the class.
Annotate your object model for parent/child relationships as described in Annotating an Object Model for Parent/Child Relationships (Aggregatable).
Example
In the example below, the ThreadAffineAttribute has been added to the class.
[ThreadAffine]
public class OrderService
{
public void Process( int sequence )
{
Console.WriteLine( "sequence {0}", sequence );
Console.WriteLine( "sleeping for 500 ms" );
Thread.Sleep( 500 );
}
}
Rules enforced by the Thread-Affine aspect
Except for the types of fields, the ThreadAffineAttribute aspect does not verify your code at build time. Unlike other threading models, it allows public and internal fields. Instead, it injects code that enforces the model at run time. If it detects that the object is being accessed from a different thread than the one that created it, the aspect will throw a ThreadMismatchException exception. This verification happens only when run-time verification is enabled, which is the default in the Debug build. See Enabling and Disabling Run-Time Verification.
To test the thread-affine OrderService class, we can run the following code:
static void Main()
{
var orderService = new OrderService();
orderService.Process( 1 );
var backgroundTask = Task.Run( () =>
{
try
{
orderService.Process( 2 );
}
catch ( Exception ex )
{
Console.WriteLine( $"{ex.GetType().Name}: {ex.Message}" );
}
} );
backgroundTask.Wait();
}
The output of this program is:
sequence 1
sleeping for 500 ms
ThreadMismatchException: An attempt was made to access the object from an invalid thread.
The orderService.Process(1) call succeeds because the orderService instance is created (with the new keyword) and used on the same thread.
Then, a background thread executes the orderService.Process(2) method, and the program writes the exception to the console. A ThreadMismatchException exception is thrown because the orderService instance was created on the main thread and the background thread attempted to use it. Because the OrderService class has been marked with the ThreadAffineAttribute attribute, only the thread that creates an instance of it can access that instance.
Working with object trees
Because the Thread-Affine model is an implementation of the Aggregatable pattern, all of the same behaviors of the AggregatableAttribute are available. For more information regarding object trees, see Parent/Child, Visitor and Disposable.
Note
Once you have established your parent-child relationships, you will need to apply compatible threading models to the child classes. You will want to refer to the Compatibility of Threading Models article to determine which threading model will work for the children of the Thread-Affine object.
See Also
Other Resources
Compatibility of Threading Models
Reference
ThreadAffineAttribute
ThreadMismatchException
PrivateThreadAwareAttribute
ChildAttribute