Skip to content

C# 多线程 05-使用 C#6.0 08-自定义 awaitable 类型

🏷️ 《C# 多线程》

awaitable 表达式要求

为了与 await 操作符保持兼容,类型应当遵守在 C# 规则说明中规定的一些要求。

我安装的是 VS2017,其路径为:

bash
*C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC#\Specifications\1033     
 [CSharp Language Specification.docx](/uploads/userfiles/e671fced-86e2-4d56-a20a-d79aab766aa7/files/2017/08/CSharp Language Specification.docx)*

7.7.7.1 Awaitable expressions

The task of an await expression is required to be awaitable. An expression t is awaitable if one of the following holds:
Await 表达式的任务被要求是 awaitable。如果一个表达式 t 满足下面任意一条则认为是 awaitalbe 的:

  • t is of compile time type dynamic
    t 是动态编译时的类型
  • t has an accessible instance or extension method called GetAwaiter with no parameters and no type parameters, and a return type A for which all of the following hold:
    t 有一个名为 GetAwaiter 的可访问的实例或扩展方法。该方法没有参数和类型参数,并且返回值类型 A 满足以下所有条件
    • A implements the interface System.Runtime.CompilerServices.INotifyCompletion (hereafter known as INotifyCompletion for brevity)
      A 实现了 System.Runtime.CompilerServices.INotifyCompletion 接口(为简单起见,以后简称为 INotifyCompletion)
    • A has an accessible, readable instance property IsCompleted of type bool
      A 有一个可访问的、可读的类型为 bool 的实例属性 IsCompleted。
    • A has an accessible instance method GetResult with no parameters and no type parameters
      A 有一个名为 GetResult 的可访问的实例方法,该方法没有任何参数和类型参数。

The purpose of the GetAwaiter method is to obtain an awaiter for the task. The type A is called the awaiter type for the await expression.
The purpose of the IsCompleted property is to determine if the task is already complete. If so, there is no need to suspend evaluation.
The purpose of the INotifyCompletion.OnCompleted method is to sign up a “continuation” to the task; i.e. a delegate (of type System.Action) that will be invoked once the task is complete.
The purpose of the GetResult method is to obtain the outcome of the task once it is complete. This outcome may be successful completion, possibly with a result value, or it may be an exception which is thrown by the GetResult method.

示例代码

csharp
/// <summary>
/// 自定义 awaitable 类型
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
    Task t = AsynchronousProcessing();
    t.Wait();

    Console.ReadLine();
}

static async Task AsynchronousProcessing()
{
    var sync = new CustomAwaitable(true);
    string result = await sync;
    // Completed synchronously
    Console.WriteLine(result);

    var async = new CustomAwaitable(false);
    result = await async;
    // Task is running on a thread id 3. Is thread pool thread: True
    Console.WriteLine(result);
}

/// <summary>
/// 类型 t
/// </summary>
class CustomAwaitable
{
    private readonly bool _completeSynchronously;

    public CustomAwaitable(bool completeSynchronously)
    {
        _completeSynchronously = completeSynchronously;
    }

    /// <summary>
    /// t 有一个名为 GetAwaiter 的可访问的实例或扩展方法
    /// </summary>
    /// <returns>类型 A</returns>
    public CustomAwaiter GetAwaiter()
    {
        return new CustomAwaiter(_completeSynchronously);
    }
}

/// <summary>
/// 类型 A 实现了 System.Runtime.CompilerServices.INotifyCompletion 接口
/// </summary>
class CustomAwaiter : INotifyCompletion
{
    private string _result = "Completed synchronously";
    private readonly bool _completeSynchronously;

    /// <summary>
    /// A 有一个可访问的、可读的类型为 bool 的实例属性 IsCompleted
    /// 如果 IsCompleted 属性返回 true,则只需同步调用 GetResult 方法
    /// </summary>
    public bool IsCompleted => _completeSynchronously;

    public CustomAwaiter(bool completeSynchronously)
    {
        _completeSynchronously = completeSynchronously;
    }

    /// <summary>
    /// A 有一个名为 GetResult 的可访问的实例方法,该方法没有任何参数和类型参数。
    /// </summary>
    /// <returns></returns>
    public string GetResult()
    {
        return _result;
    }

    public void OnCompleted(Action continuation)
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            Thread.Sleep(TimeSpan.FromSeconds(1));
            _result = GetInfo();
            continuation?.Invoke();
        });
    }

    private string GetInfo()
    {
        return $"Task is running on a thread id {Thread.CurrentThread.ManagedThreadId}. " +
            $"Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
    }
}

运行结果

Completed synchronously
Task is running on a thread id 3. Is thread pool thread: True

TIP

该实现只适用于教学目的。当你编写异步函数时,最自然的方式好还是使用标准的 Task 类型。