Open sandboxFocusImprove this doc

THR036: Background Method in a Thread-Affine Class

Severity Error
Reported by PostSharp.Patterns.Threading
Message Cannot add [Background] to method "<Method>" because the [ThreadAffine] threading model is applied to the type "<Type>". Apply [Background] to a static method, or to a method of another type.

Cause

The ThreadAffineAttribute threading model allows an object to be accessed only from the thread that created it. The BackgroundAttribute aspect executes a method in a thread of the thread pool. An instance method of a thread-affine object with this aspect would therefore always access the object from the wrong thread, so PostSharp reports THR036.

The check applies only to instance methods. A static method does not belong to an object, so it can have the BackgroundAttribute aspect.

In the following example, the BuildReportAsync instance method of the thread-affine OrderListView class has the BackgroundAttribute aspect.

using System;
using System.Threading;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;

namespace Errors.THR036_Error;

[ThreadAffine]
public class OrderListView
{
    private int orderCount;

    public int OrderCount => this.orderCount;

    public void AddOrder()
    {
        this.orderCount++;
    }

    // An instance method of a thread-affine object cannot run in a background thread.
    [Background]
    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 void Main()
    {
        var view = new OrderListView();
        view.AddOrder();
        view.AddOrder();

        var report = view.BuildReportAsync( view.OrderCount ).GetAwaiter().GetResult();
        Console.WriteLine( report );
    }
}
error THR036: THR036_Error.cs(22,31): Cannot add [Background] to method "Errors.THR036_Error.OrderListView.BuildReportAsync(System.Int32)" because the [ThreadAffine] threading model is applied to the type "Errors.THR036_Error.OrderListView". 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 object, make the method static and pass it the data it needs as parameters.
  • Otherwise, move the background work to a method of another class that does not have the ThreadAffineAttribute threading model, and update the thread-affine object from its own thread when the work completes.

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.THR036_Fixed;

[ThreadAffine]
public class OrderListView
{
    private int orderCount;

    public int OrderCount => this.orderCount;

    public void AddOrder()
    {
        this.orderCount++;
    }

    // A static method does not access the thread-affine object, 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 void Main()
    {
        var view = new OrderListView();
        view.AddOrder();
        view.AddOrder();

        var report = OrderListView.BuildReportAsync( view.OrderCount ).GetAwaiter().GetResult();
        Console.WriteLine( report );
    }
}
Report of 2 orders, built in the thread pool: True

See Also

Reference

BackgroundAttribute
ThreadAffineAttribute

Other Resources

Thread Affine Threading Model
Dispatching a Method to Background