| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | Cannot add [Background] to method "<Method>" because the [Actor] threading model is applied to the type "<Type>". Apply [Background] to a static method, or to a method of another type. |
Cause
The ActorAttribute threading model executes the methods of an actor one at a time, in the context of the actor, so that the actor never needs a lock. The BackgroundAttribute aspect executes a method in a thread of the thread pool, outside of this context. An instance method of an actor with this aspect could therefore access the state of the actor concurrently with its other methods, so PostSharp reports THR037.
The check applies only to instance methods. A static method does not access the state of an actor, so it can have the BackgroundAttribute aspect.
In the following example, the BuildReportAsync instance method of the OrderProcessor actor has the BackgroundAttribute aspect. The async instance methods of the actor also have the ReentrantAttribute attribute, which the ActorAttribute threading model requires (THR017).
using System;
using System.Threading;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;
namespace Errors.THR037_Error;
[Actor]
public class OrderProcessor
{
private int processedCount;
[Reentrant]
public async Task<int> ProcessAsync( string order )
{
await Task.Yield();
this.processedCount++;
return this.processedCount;
}
// An instance method of an actor cannot run in a background thread.
[Background]
[Reentrant]
public async Task<string> BuildReportAsync( int orderCount )
{
await Task.Yield();
return $"Report of {orderCount} orders, built in the thread pool: {Thread.CurrentThread.IsThreadPoolThread}";
}
}
public static class Program
{
public static async Task Main()
{
var processor = new OrderProcessor();
await processor.ProcessAsync( "Order 1" );
var count = await processor.ProcessAsync( "Order 2" );
Console.WriteLine( await processor.BuildReportAsync( count ) );
}
}
error THR037: THR037_Error.cs(25,31): Cannot add [Background] to method "Errors.THR037_Error.OrderProcessor.BuildReportAsync(System.Int32)" because the [Actor] threading model is applied to the type "Errors.THR037_Error.OrderProcessor". Apply [Background] to a static method, or to a method of another type.
Resolution
- When the background work does not need the state of the actor, make the method
staticand pass it the data it needs as parameters. - When the method needs the state of the actor, remove the BackgroundAttribute aspect: the caller of a public
asyncmethod of an actor does not wait for the method to execute in the context of the actor. - Otherwise, move the background work to a method of another class.
In the following example, BuildReportAsync is a static method that receives the number of orders.
using System;
using System.Threading;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;
namespace Errors.THR037_Fixed;
[Actor]
public class OrderProcessor
{
private int processedCount;
[Reentrant]
public async Task<int> ProcessAsync( string order )
{
await Task.Yield();
this.processedCount++;
return this.processedCount;
}
// A static method does not access the state of the actor, so it can run in a background thread.
[Background]
public static async Task<string> BuildReportAsync( int orderCount )
{
await Task.Yield();
return $"Report of {orderCount} orders, built in the thread pool: {Thread.CurrentThread.IsThreadPoolThread}";
}
}
public static class Program
{
public static async Task Main()
{
var processor = new OrderProcessor();
await processor.ProcessAsync( "Order 1" );
var count = await processor.ProcessAsync( "Order 2" );
Console.WriteLine( await OrderProcessor.BuildReportAsync( count ) );
}
}
Report of 2 orders, built in the thread pool: True
See Also
Reference
BackgroundAttribute
ActorAttribute
Other Resources