uid | title | product | categories | summary |
---|---|---|---|---|
threading-waiving-verification |
Opting In and Out From Thread Safety |
postsharp |
PostSharp;AOP;Metaprogramming |
The document explains how to opt in and out of thread safety in PostSharp by using the ExplicitlySynchronizedAttribute and EntryPointAttribute on methods and fields. It also warns about potential multithreading defects. |
By default, PostSharp enforces thread safety for all instance fields and all public and internal methods of any class to which you applied a threading model.
However, there are times when you want to opt-out from this mechanism for a specific field or method. A typical reason is that access to the field is synchronized manually using a different mechanism.
This section shows how to override the default thread safety implemented by PostSharp.
To disable enforcement of the class-level threading model for a specific method, add the xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute attribute to that method.
In the following example, this custom attribute allows us to implement the ToString
in a class that respects the Actor model. Without the custom attribute, this would not have been possible because non-void public methods must have the async
keyword.
[Actor]
class Player
{
private readonly string name;
[ExplicitlySynchronized]
public override string ToString()
{
return this.name;
}
}
When used on a method, the xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute attribute has several effects:
-
Lock-based aspects such as xref:PostSharp.Patterns.Threading.SynchronizedAttribute or xref:PostSharp.Patterns.Threading.ReaderWriterSynchronizedAttribute will not attempt to acquire a lock before executing this method.
-
Accesses to fields are not verified during the whole execution of the method (for the current thread).
-
All build-time verifications are disabled for this method.
Warning
By using the xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute custom attribute, you are significantly increasing the risk that multithreading defects in user code go undetected by PostSharp. Code using xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute should be more carefully covered by reviews and tests.
To disable enforcement of the class-level threading model for a specific field, add the xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute attribute to the field:
[Actor]
class MyActor
{
[ExplicitlySynchronized]
int counter;
public void FooBar()
{
// This line would throw an exception without [ExplicitlySynchronized].
Task.Factory.StartNew(() => Interlocked.Increment( ref this.counter ));
}
}
When used on a field, the xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute attribute has several effects:
-
Accesses to the field are never verified
-
All build-time verifications are disabled for this method.
By default, thread safety is ensured when a thread first invokes a public or internal method of an object. The underlying motivation is that public and internal methods are the primary way how a thread can enter an object. Another way is to enter an object through a delegate call to a private method. By default, PostSharp does not ensure thread safety for private methods. If you register a callback method, you need to add the xref:PostSharp.Patterns.Threading.EntryPointAttribute custom attribute on this method.
In the following code snippet, the OnCreated
method is invoked from a background thread by the FileSystemWatcher
class. The InputQueueWatcher
is thread-safe thanks to the xref:PostSharp.Patterns.Threading.SynchronizedAttribute aspect.
[Synchronized]
class InputQueueWatcher
{
FileSystemWatcher watcher;
[Child]
AdvisableCollection<string> files = new AdvisableCollection<string>();
public InputQueueWatcher(string path)
{
this.watcher = new FileSystemWatcher();
this.watcher.Path = path;
this.watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
this.watcher.Filter = "*.xml";
this.watcher.Created += new FileSystemEventHandler(OnCreated);
}
[EntryPoint]
private void OnCreated(object source, FileSystemEventArgs e)
{
// Without [EntryPoint], the following line would throw ThreadAccessException.
this.files.Add(e.FullPath);
}
public ICollection Files { get { return this.files; } }
}
Reference
xref:PostSharp.Patterns.Threading.ExplicitlySynchronizedAttribute
xref:PostSharp.Patterns.Threading.EntryPointAttribute
Other Resources
xref:thread-unsafe
xref:reader-writer-synchronized
xref:ui-dispatching
xref:actor