Open sandboxFocusImprove this doc

THR001: Method Cannot Be Dispatched in Non-Blocking Mode

Severity Error
Reported by PostSharp.Patterns.Threading
Message Method "<Method>" cannot be enhanced with the [Dispatched] aspect in non-blocking mode. When the ExecutionMode property is NonBlocking or NonBlockingContextSwitch, for example with [Dispatched(true)], this aspect can only be applied 1) to void methods with no ref, out or in parameters; 2) to async methods with return type void, Task or Task<T>.

Cause

The DispatchedAttribute aspect executes a method in the thread of the object, typically the UI thread. In non-blocking mode, the caller does not wait for the method: it enqueues the call and continues immediately. The aspect is in non-blocking mode when you pass true to its constructor, or when you set its ExecutionMode property to NonBlocking or NonBlockingContextSwitch.

Because the caller does not wait, it cannot receive a return value or the values of ref and out parameters. PostSharp therefore reports THR001 when a method dispatched in non-blocking mode is:

  • a method without the async keyword whose return type is not void, including a method that returns a Task without being async;
  • an async method whose return type is neither void nor Task or Task<T>, for example ValueTask<T>;
  • a method with a ref, out or in parameter.

In the following example, the AddOrder method returns the number of orders, but the aspect is in non-blocking mode.

using System;
using System.Collections.Generic;
using System.Threading;
using PostSharp.Patterns.Threading;

namespace Errors.THR001_Error;

public class OrderListView
{
    private readonly List<string> orders = new();

    // In non-blocking mode, the caller does not wait for the return value.
    [Dispatched( true )]
    public int AddOrder( string order )
    {
        this.orders.Add( order );

        return this.orders.Count;
    }
}

public static class Program
{
    public static void Main()
    {
        // A console application has no UI thread, so the view uses the synchronization context of the main thread.
        SynchronizationContext.SetSynchronizationContext( new SynchronizationContext() );

        var view = new OrderListView();
        Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 1" )}" );
        Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 2" )}" );
    }
}
error THR001: THR001_Error.cs(14,16): Method "Errors.THR001_Error.OrderListView.AddOrder(System.String)" cannot be enhanced with the [Dispatched] aspect in non-blocking mode. When the ExecutionMode property is NonBlocking or NonBlockingContextSwitch, for example with [Dispatched(true)], this aspect can only be applied 1) to void methods with no ref, out or in parameters; 2) to async methods with return type void, Task or Task<T>.

Resolution

  • When the caller needs the result of the method, use the blocking mode, which is the default: remove the true argument, or set ExecutionMode to Blocking. The caller then waits for the method to complete in the thread of the object.
  • When the caller should not wait, make the method async and return a Task or a Task<T>, or return void and remove the ref, out and in parameters.

In the following example, the aspect is in blocking mode, so the caller receives the number of orders.

using System;
using System.Collections.Generic;
using System.Threading;
using PostSharp.Patterns.Threading;

namespace Errors.THR001_Fixed;

public class OrderListView
{
    private readonly List<string> orders = new();

    // In blocking mode, the caller waits for the method to complete and receives its return value.
    [Dispatched]
    public int AddOrder( string order )
    {
        this.orders.Add( order );

        return this.orders.Count;
    }
}

public static class Program
{
    public static void Main()
    {
        // A console application has no UI thread, so the view uses the synchronization context of the main thread.
        SynchronizationContext.SetSynchronizationContext( new SynchronizationContext() );

        var view = new OrderListView();
        Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 1" )}" );
        Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 2" )}" );
    }
}
Orders in the view: 1
Orders in the view: 2

See Also

Reference

DispatchedAttribute
DispatchedExecutionMode

Other Resources

Dispatching a Method to the UI Thread