PostSharp
[Threading]
Write verifiable thread-safe code in .NET without exploding your brain.
- Dramatically reduce complexity of multi-threaded code. Free up your brain for business logic.
- Reduce random defects. Most problems are detected at build time or in single-threaded unit tests.
- Keep your business logic crystal-clear, easier to modify after release 1.0.

Instead of locks, events and queues:
architectural-level decisions
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Per architectural decision, any implementation of IWizardController will run // exclusively on the UI thread. Nobody ever needs to think about this again. [ThreadAffine] [Dispatched(AttributeInheritance = MulticastInheritance.Strict)] public interface IWizardController { Task BranchAsync(params IWizardPart[] parts); Task GoToNextPageAsync(); Task CloseAsync(bool success); } // Per architectural decision, any IProgressTask must be immutable and report // progress through an IProgressTaskObserver. [Immutable] [NamingConvention("*Task")] public interface IProgressTask { string Caption { get; } Task ExecuteAsync(IProgressTaskObserver observer); }
The problem with locks – When you're using locks, events and queues, you are taking decisions at the level of individual fields. The mental model is so complex that it hardly fits people's brain, and bugs are unavoidable. Senior developers are overwhelmed with completely unnecessary problems.
Higher abstraction – With PostSharp Threading, multi-threading becomes a conceptual and architectural decision. One you can take at the level of a whole class hierarchy or namespace.
Free up your brain – You no longer have to take hundreds of micro-decisions. The mental model fits everybody's brain. Most team members must no longer care about multi-threading. Your most experienced developers can now work on other thorny problems.
Choose the right threading model
There is no silver bullet. Learn the different threading models and decide which one is appropriate for each part of your application.
Immutable
Ensures that the object or its children cannot be modified after the constructor of the top object has completed, otherwise a run-time exception is thrown. doc
Synchronized
Probably the simplest and more popular model. All public methods execute within a lock. A run-time exception is thrown if the object is accessed from a thread that does not own the lock. doc
Thread Affine
Ensures that the object is only accessed from the UI thread, or throws an exception. All system UI types are thread affine so this is a good choice for all your controls and windows. doc
Freezable
Similar to immutable, but a Freeze
method must be called. Also throws
an exception if the object is accessed from a different thread than the constructor
one before Freeze
has been called.
doc
Reader-Writer Synchronized
Reader methods can run concurrently, but writer methods get exclusive access. A good choice for objects that are bound to the UI but need to be accessed from a background thread. Property accessors automatically acquire the right level of lock but methods must be annotated manually. doc
Actor
All calls to the actor are enqueued and executed one after the other in a background thread.
Perfectly integrates with async
/await
.
Replaces most queues.
doc
Fail fast, fail sooner
Model validation – Once you choose a model, PostSharp will ensure that you are not breaking the rules. Get deterministic build-time or run-time errors instead of risking data races.
Make more of your unit tests – With randomness removed, even single-threaded unit tests will expose most threading issues.
Lean release builds – Run-time validation is only injected into your debug builds by default, so your release builds are still fast and don't crash on assertion failures.

Support for children objects
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[ReaderWriterSynchronized] public class Order { [Child] // Order lines will share the same lock. public AdvisableCollection<OrderLine> Lines { get; } = new AdvisableCollection<OrderLine>(); // Requires the guarantee that the whole entity will not be modified. public decimal Subtotal => this.Lines.Sum(l => l.Amount); public decimal Discount { get; set; } public decimal Total => this.Total - this.Discount; [Writer] // Will execute with exclusive access. public void Merge(Order other) { foreach (var otherLine in other.Lines) { this.Lines.Add(otherLine.Clone()); } } }
Many business entities are composed of several instances of C# classes. In this case, locks must be shared by the whole entity.
Parent-child relationships —
Simply mark children objects with the [Child]
attribute and they will automatically inherit
the threading model and the lock of the parent instance. doc
Flexible collection types — PostSharp provides collection types that will work with any of the supported threading models. You don't need to change the collection types when you change the threading models. doc
