首页 文章

如何写一个“等待”的方法?

提问于
浏览
53

我在.Net框架中看到过调用异步方法,例如this one,调用 HttpClient.GetStringAsync() .

我不太清楚的是这种方法会发生什么,以及我如何编写自己的“等待”方法 . 它是否像包装我想在Task中异步运行的代码并返回那样简单?

5 回答

  • 9

    这很简单

    Task.Run(() => ExpensiveTask());
    

    为了使它成为一个等待的方法:

    public Task ExpensiveTaskAsync()
    {
        return Task.Run(() => ExpensiveTask());
    }
    

    这里重要的是返回一个任务 . 该方法甚至不必标记为异步 . (只是进一步阅读它进入图片)

    现在这可以称为

    async public void DoStuff()
    {
        PrepareExpensiveTask();
        await ExpensiveTaskAsync();
        UseResultsOfExpensiveTask();
    }
    

    请注意,此处方法签名为 async ,因为该方法可能会将控制权返回给调用者,直到 ExpensiveTaskAsync() 返回 . 而且,在这种情况下昂贵意味着耗时,如网络请求或类似的 . 要将大量计算发送到另一个线程,通常最好使用"old"方法,即 System.ComponentModel.BackgroundWorker 用于GUI应用程序或 System.Threading.Thread .

  • 73

    我将如何编写自己的“等待”方法?它是否像包装我想在Task中异步运行的代码并返回那样简单?

    这是一个选项,但它实际上为您提供了异步代码的许多优点 . 有关更多详细信息,请参阅Stephen Toub's Should I expose asynchronous wrappers for synchronous methods?

    一般来说,方法是不可取的,类型是 . 如果您希望能够编写类似 await MyMethod() 的内容,则 MyMethod() 必须返回 TaskTask<T> 或自定义 await able类型 . 使用自定义类型是一种罕见的高级方案;使用 Task ,您有几个选择:

    • 使用 asyncawait 编写方法 . 这对于异步编写操作很有用,但它不能用于最内部的 await 能够调用 .

    • 使用 Task 上的某个方法创建 Task ,如 Task.Run()Task.FromAsync() .

    • 使用TaskCompletionSource . 这是最通用的方法,它可以用于从将来发生的任何事情中创建 await able方法 .

  • 1

    ...我将如何编写自己的“等待”方法 .

    返回 Task 不是唯一的方法 . 您可以选择创建自定义awaiter(通过实现 GetAwaiterINotifyCompletion),这是一个很好的读取:"Await anything" . 返回自定义awaiters的.NET API示例: Task.Yield()Dispatcher.InvokeAsync .

    我有一些自定义awaiters herehere的帖子,例如:

    // don't use this in production
    public static class SwitchContext
    {
        public static Awaiter Yield() { return new Awaiter(); }
    
        public struct Awaiter : System.Runtime.CompilerServices.INotifyCompletion
        {
            public Awaiter GetAwaiter() { return this; }
    
            public bool IsCompleted { get { return false; } }
    
            public void OnCompleted(Action continuation)
            {
                ThreadPool.QueueUserWorkItem((state) => ((Action)state)(), continuation);
            }
    
            public void GetResult() { }
        }
    }
    
    // ...
    
    await SwitchContext.Yield();
    
  • 10

    是的,从技术上讲,您只需要从 async 方法返回 TaskTask<Result> 来实现一个等待的方法 .

    这支持Task-Based Asynchronous Pattern .

    但是,有几种方法可以实现TAP . 有关详细信息,请参阅Implementing the Task-based Asynchronous Pattern .

    (但当然,所有这些实现仍然会返回 TaskTask<Result> . )

  • 3

    只需将您的方法转换为任务 . 像@Romiox我通常使用这个扩展

    public static partial class Ext
    {
        #region Public Methods
         public static Task ToTask(Action action)
        {
            return Task.Run(action);
        }
        public static Task<T> ToTask<T>(Func<T> function)
        {
            return Task.Run(function);
        }
        public static async Task ToTaskAsync(Action action)
        {
            await Task.Run(action);
        }
        public static async Task<T> ToTaskAsync<T>(Func<T> function)
        {
            return await Task.Run(function);
        }
        #endregion Public Methods
    }
    

    现在让我们说你有

    void foo1()

    void foo2(int i1)

    int foo3()

    int foo4(int i1)

    ...然后你可以像@Romiox一样声明你的[异步方法]

    async Task foo1Async(){
       return await Ext.ToTask(()=>foo1());
    }
    async Task foo2Async(int i1){
       return await Ext.ToTask(()=>foo2(i1));
    }
    async Task<int> foo3Async(){
       return await Ext.ToTask(()=>foo3());
    }
    async Task<int> foo4Async(int i1){
        return await Ext.ToTask(()=>foo4(i1));
    }
    

    要么

    async Task foo1Async(){
     return await Ext.ToTaskAsync(()=>foo1());
    }
    async Task foo2Async(int i1){
    return await Ext.ToTaskAsync(()=>foo2(i1));
    }
    async Task<int> foo3Async(){
    return await Ext.ToTaskAsync(()=>foo3());
    }
    async Task<int> foo4Async(int i1){
    return await Ext.ToTaskAsync(()=>foo4(i1));
    }
    

    ...

    现在你使用async并等待任何fooAsync,例如foo4Async

    async Task<int> TestAsync()
    {
       ///Initial Code
       int m=3;
       ///Call the task
       var X =foo4Async(m);
       ///Between
       ///Do something while waiting comes here
       ///..
       var Result =await X;
       ///Final
       ///Some Code here
       return Result;
    }
    

相关问题