如何写一个"等待"的方法?

And*_*ens 61 c# async-await

我终于研究了async和await关键字,我有点"得到",但我见过的所有例子都在.Net框架中调用异步方法,例如这个调用HttpClient.GetStringAsync().

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

Jan*_*s F 78

这很简单

Task.Run(() => ExpensiveTask());
Run Code Online (Sandbox Code Playgroud)

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

public Task ExpensiveTaskAsync()
{
    return Task.Run(() => ExpensiveTask());
}
Run Code Online (Sandbox Code Playgroud)

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

现在这可以称为

async public void DoStuff()
{
    PrepareExpensiveTask();
    await ExpensiveTaskAsync();
    UseResultsOfExpensiveTask();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 正确的做法是,最自然和通常的方法是编写一个返回`Task`或`Task <>`的方法.但从技术上讲,你也可以编写一个返回`YourOwnType`的方法,前提是`YourOwnType`有一个名为`GetAwaiter()`的公共无参数非静态实例方法,其返回类型是合适的(在其他地方查找详细信息).因此`await`有点像`foreach`,它适用于任何具有合适公共方法的类型. (14认同)
  • @JeppeStigNielsen`foreach`需要`IEnumerable`.我有点失望的是,当一个界面更适合该语言时,`await`使用duck typing.它是'用于编译器'的事实是一个糟糕的借口. (2认同)
  • @Gusdor为什么,`foreach`不需要任何接口!试试这些类:`class ForeachMe {public StrangeType GetEnumerator(){return new StrangeType(); class StrangeType {public bool MoveNext(){return true; public DateTime Current {get {return DateTime.Now; `用它们,代码`foreach(var x in new ForeachMe()){Console.WriteLine(x); }`会工作得很好. (2认同)
  • @Gusdor权威来源是官方C#语言规范中的[foreach语句(旧版本)](http://msdn.microsoft.com/en-us/library/aa664754.aspx).在[最新版本](http://msdn.microsoft.com/en-us/library/ms228593.aspx)中找到相同的部分.这是明确规定的; 该类型不需要实现`IEnumerable`或`IEnumerable <>`. (2认同)

svi*_*ick 13

我将如何编写自己的"等待"方法?它是否像包装我希望在a中异步运行Task并返回的代码一样简单?

这是一个选项,但它很可能不是您想要做的,因为它实际上并没有为您提供异步代码的许多优点.有关更多详细信息,请参阅Stephen Toub 应该为同步方法公开异步包装器吗?

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

  • async和编写你的方法await.这对于异步编写动作很有用,但它不能用于最内部的await调用.
  • Task使用其中一种方法创建Task,例如Task.Run()Task.FromAsync().
  • 使用TaskCompletionSource.这是最通用的方法,它可以用于await从将来发生的任何事情创建有效的方法.


nos*_*tio 11

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

回归Task不是唯一的方法.你可以选择创建一个自定义awaiter(通过实现GetAwaiterINotifyCompletion),这是一个很好的阅读:"等待任何事情".返回自定义awaiters的.NET API示例:Task.Yield(),Dispatcher.InvokeAsync.

在这里这里有一些定制的等待者的帖子,例如:

// 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();
Run Code Online (Sandbox Code Playgroud)

  • `//不要在生产中使用它-到底为什么? (2认同)

Mat*_*son 5

是的,从技术上讲,您只需要从方法中返回 aTask或即可实现可等待的方法。Task<Result>async

这支持基于任务的异步模式

但是,有多种实施 TAP 的方法。有关详细信息,请参阅实现基于任务的异步模式

(但所有这些实现仍然返回Taskor Task<Result>,当然。)


Wal*_*.K. 5

只需将您的方法转换为任务。像@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)
    {
        return await Task.Run(action);
    }
    public static async Task<T> ToTaskAsync<T>(Func<T> function)
    {
        return await Task.Run(function);
    }
    #endregion Public Methods
}
Run Code Online (Sandbox Code Playgroud)

现在让我们说你有

void foo1()...

void foo2(int i1)...

int foo3()...

int foo4(int i1)...
Run Code Online (Sandbox Code Playgroud)

...

然后你可以像@Romiox一样声明你的[async Method]

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));
}
Run Code Online (Sandbox Code Playgroud)

或者

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));
}
Run Code Online (Sandbox Code Playgroud)

...

现在您可以对任何 fooAsync 方法使用 async 和 await,例如 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;
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

38360 次

最近记录:

5 年,9 月 前