从非异步方法调用异步方法

本文关键字:异步方法 调用 | 更新日期: 2023-09-27 18:17:22

我尝试的以下代码的每个变体都不起作用-无论是DoSomething() : void和按写的方式调用,还是DoSomething() : Task和用TaskEx.RunEx()调用,一些涉及.GetAwaiter().GetResult()的尝试。看到的错误包括:"Start may not be called on a task with null action", "RunSynchronously may not be called on a task unbound to a delegate""The task has not yet completed"

class Program
{
    static void Main(string[] args) // Starting from a non-async method
    {
        DoSomething();
        Console.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
    static async void DoSomething()
    {
        Console.WriteLine("Starting DoSomething ...");
        var x = await PrepareAwaitable(1);
        Console.WriteLine("::" + x);
        var y = await PrepareAwaitable(2);
        Console.WriteLine("::" + y);
    }
    static Task<string> PrepareAwaitable(int id)
    {
        return new Task<string>(() =>
        {
            return "Howdy " + id.ToString();
        });
    }
}
输出:

start DoSomething…

按任意键退出。

PrepareAwaitable 's Task 's Action后面会更复杂。当这个动作完成时,无论需要多长时间,我都希望Task(或其他框架机制)通过将"Howdy ..."分配给x,然后再分配给y来恢复。我真正想做的是拦截等待的对象,处理它们,并在稍后我控制的时间内恢复到具有结果(xy)的延续。但是我还没有迈出那么大的一步,所以我试着从更小的开始

从非异步方法调用异步方法

首先,阅读基于任务的异步模式文档。在My Documents'Microsoft Visual Studio Async CTP'Documentation下。本文档描述了如何设计可被await自然使用的api。

其次,要认识到Task类和相关api的几个方面不再真正适用于新的异步世界。Task最初是作为TPL的核心编写的。事实证明,它非常适合异步编程,因此微软使用它而不是创建一个新的"承诺"。类。但是许多方法是保留的,由TPL使用,但不需要异步编程。

要回答这个名义上的问题,在当前的异步CTP中,混合同步和异步代码并不是很简单。我编写了一个库,其中包含一个Task.WaitAndUnwrapException扩展方法,这使得从同步代码调用异步代码变得容易。你可能对我的AsyncContext课也感兴趣,这使得"按任意键"引发不必要的。

您返回的任务尚未开始(即,它们是"冷"任务);尝试用以下代码替换PrepareAwaitable代码:

static Task<string> PrepareAwaitable(int x)
{
    return Task.Factory.StartNew<string>(() =>
    {
        return "Howdy " + x.ToString();
    });
}

这真的不清楚你想要实现什么,因为没有异步发生。例如,这将编译并运行,但我不知道这是否是您想要的:

using System;
using System.Threading.Tasks;
class Program
{
    static void Main(string[] args)
    {
        DoSomething();
        Console.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }
    static async void DoSomething()
    {
        Console.WriteLine("Starting DoSomething ...");
        var x = await PrepareAwaitable(1);
        Console.WriteLine("::" + x);
        var y = await PrepareAwaitable(2);
        Console.WriteLine("::" + y);
    }
    static async Task<string> PrepareAwaitable(int x)
    {
        return "Howdy " + x;
    }
}

注意,这给PrepareAwaitable一个警告,因为它没有异步;没有await表达式。整个程序同步执行。PrepareAwaitable的另一个替代实现:

static Task<string> PrepareAwaitable(int x)
{
    return TaskEx.Run(() => "Howdy " + x);
}

那更像你想要的吗?

添加GetAwaiter().GetResult()后,您的异步任务应该工作。但是为了消除诸如"……"之类的错误。而不是在null action"任务上调用,您只需让Task<IHttpActionResult>返回Ok(true)而不是Ok()。它为我修复了getreresult()问题!