C# 如何写一个;“等待”;方法?

C# 如何写一个;“等待”;方法?,c#,async-await,C#,Async Await,我最后研究了async&await关键字,我有点“获取”,但我看到的所有示例都调用.Net framework中的异步方法,例如调用HttpClient.GetStringAsync() 我不太清楚的是,在这种方法中会发生什么,以及我将如何编写自己的“等待”方法。它是否像包装我希望在任务中异步运行的代码并返回它那样简单?它就像 Task.Run(() => ExpensiveTask()); 要使其成为一种值得期待的方法: public Task ExpensiveTaskAsync()

我最后研究了async&await关键字,我有点“获取”,但我看到的所有示例都调用.Net framework中的异步方法,例如调用
HttpClient.GetStringAsync()

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

它就像

Task.Run(() => ExpensiveTask());
要使其成为一种值得期待的方法:

public Task ExpensiveTaskAsync()
{
    return Task.Run(() => ExpensiveTask());
}
这里最重要的是返回一个任务。该方法甚至不必标记为异步。(只需再多读一点就可以了)

现在这可以称为

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

注意,这里的方法签名是
async
,因为该方法可能会将控制权返回给调用方,直到
ExpensiveTaskAsync()
返回为止。此外,在这种情况下,昂贵意味着耗时,如web请求或类似请求。要将繁重的计算发送到另一个线程,通常最好使用“旧”方法,即GUI应用程序的
System.ComponentModel.BackgroundWorker
System.Threading.thread
是,从技术上讲,您只需要从
async
方法返回
任务
任务
,即可实现可等待的方法

这支持了这一点

然而,有几种实现TAP的方法。有关详细信息,请参阅

(当然,所有这些实现仍然返回
Task
Task

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

返回
任务
不是唯一的方法。您可以选择创建自定义等待器(通过实现
GetAwaiter
和),下面是一个很好的阅读:。返回自定义等待程序的.NET API示例:
Task.Yield()
Dispatcher.InvokeAsync

我有一些关于定制等待者的帖子,例如:

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

这是一种选择,但它很可能不是您想要做的,因为它实际上没有给您异步代码的许多优点。有关详细信息,请参阅

通常,方法是不可等待的,类型是可等待的。如果希望能够编写类似于
await MyMethod()
的内容,则
MyMethod()
必须返回
Task
Task
或自定义
await
able类型。使用自定义类型是一种罕见的高级场景;使用
任务
,您有几个选项:

  • 使用
    async
    wait
    编写方法。这对于异步编写操作很有用,但不能用于最内部的
    wait
    able调用
  • 使用
    Task
    上的方法之一创建
    Task
    ,如
    Task.Run()
    Task.fromsync()
  • 使用。这是最通用的方法,它可以用来从将来发生的任何事情中创建
    await
    able方法

只需将方法转换为任务即可。像@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
}

然后你可以声明你的[async方法],比如@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));
}
异步任务foo1Async() { return wait Ext.ToTask(()=>foo1()); } 异步任务foo2Async(inti1) { return wait Ext.ToTask(()=>foo2(i1)); } 异步任务foo3Async() { return wait Ext.ToTask(()=>foo3()); } 异步任务foo4Async(inti1) { return wait Ext.ToTask(()=>foo4(i1)); } 或

异步任务foo1Async() { return wait Ext.ToTaskAsync(()=>foo1()); } 异步任务foo2Async(inti1) { return wait Ext.ToTaskAsync(()=>foo2(i1)); } 异步任务foo3Async() { return wait Ext.ToTaskAsync(()=>foo3()); } 异步任务foo4Async(inti1) { return wait 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;
}
异步任务TestAsync(){
///初始代码
int m=3;
///调用任务
var X=foo4Async(m);
///中间
///等我来的时候做点什么
///..
var结果=等待X;
///决赛
///这里有一些代码
返回结果;
}

如果您不想使用
任务
,您可以编写一个完全定制的等待对象。这样的对象是一个实现方法
GetAwaiter()
的对象,返回一个实现
INotifyCompletion
的对象,该对象可以是对象本身

更多:

等待者实现:

  • IsCompleted
    是否获取状态
  • GetResult()
    获取结果
  • OnCompleted(动作延续)
    设置延续委托
等待对象包含一些实际有效负载的方法(例如,下面的方法是
Run


当然,也可以编写一个异步方法,返回
任务
任务
。异步是很好的组合方式。我只是在学习C。我在使用GetStringAsync()查看同一个示例,尽管我是一名经验丰富的Java多线程程序开发人员,但我遇到了与OP完全相同的问题。这是一个很好的问题,可能应该在MSDN的文章中解决,否则这篇文章将非常完整。正确的是,创建一个可等待的方法最自然、最常用的方法是编写一个返回
Task
Task
的方法。但从技术上讲,如果
YourOwnType
有一个名为
GetAwaiter()
的公共无参数非静态实例方法,那么您也可以编写一个返回
YourOwnType
的方法,该方法的返回类型是适当的(请在别处查找详细信息)。因此,
await
有点像
foreach
,它适用于任何具有合适公共方法的类型。很高兴知道!虽然它可能
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 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;
}
class Program {
    // Need to change the declaration of Main() in order to use 'await'
    static async Task Main () {
        // Create a custom awaitable object
        MyAwaitable awaitable = new MyAwaitable ();

        // Run awaitable payload, ignore returned Task
        _ = awaitable.Run ();

        // Do some other tasks while awaitable is running
        Console.WriteLine ("Waiting for completion...");

        // Wait for completion
        await awaitable;

        Console.WriteLine ("The long operation is now complete. " + awaitable.GetResult());
    }
}

public class MyAwaitable : INotifyCompletion {
    // Fields
    private Action continuation = null;
    private string result = string.Empty;

    // Make this class awaitable
    public MyAwaitable GetAwaiter () { return this; }

    // Implementation of INotifyCompletion for the self-awaiter
    public bool IsCompleted { get; set; }
    public string GetResult () { return result; }
    public void OnCompleted (Action continuation) {
        // Store continuation delegate
        this.continuation = continuation;
        Console.WriteLine ("Continuation set");
    }

    // Payload to run
    public async Task Run () {
        Console.WriteLine ("Computing result...");

        // Wait 2 seconds
        await Task.Delay (2000);
        result = "The result is 10";

        // Set completed
        IsCompleted = true;

        Console.WriteLine ("Result available");

        // Continue with the continuation provided
        continuation?.Invoke ();
    }
}