一尘不染

无法在控制台应用程序的“主要”方法上指定“异步”修饰符

c#

我是使用async修饰符进行异步编程的新手。我试图弄清楚如何确保Main控制台应用程序的方法实际上异步运行。

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = bs.GetList();
    }
}

public class Bootstrapper {

    public async Task<List<TvChannel>> GetList()
    {
        GetPrograms pro = new GetPrograms();

        return await pro.DownloadTvChannels();
    }
}

我知道这不是从“顶部”异步运行的。由于无法asyncMain方法上指定修饰符,因此如何在main异步方式内运行代码?


阅读 213

收藏
2020-05-19

共1个答案

一尘不染

如您所见,在VS11中,编译器将禁止使用async Main方法。在VS2010中,使用Async CTP允许(但绝不建议这样做)。

我最近在博客文章中特别提到了异步/等待异步控制台程序。以下是介绍性帖子中的一些背景信息:

如果“ await”看到awaitable尚未完成,则它将异步执行。它告诉等待的对象在完成时运行该方法的其余部分,然后从异步方法中 返回
。当等待方法将其余方法传递给等待方法时,它还将捕获当前 上下文

稍后,当awaitable完成时,它将执行async方法的其余部分(在捕获的上下文中)。

这就是在控制台程序中使用出现问题的原因async Main

请记住,在我们的介绍性帖子中,异步方法将在完成之前 返回
其调用方。这在UI应用程序(该方法仅返回到UI事件循环)和ASP.NET应用程序(该方法从线程返回但使请求保持活动状态)中完美地工作。对于Console程序,效果不是很好:Main返回OS-
因此程序退出。

一种解决方案是提供您自己的上下文-异步兼容的控制台程序的“主循环”。

如果您有一台具有异步CTP的计算机,则可以GeneralThreadAffineContext从“ 我的文档” \“ Microsoft Visual
Studio”中,使用“异步CTP” \“样本(C#测试)”单元测试\“ AsyncTestUtilities”

。或者,您可以AsyncContext我的Nito.AsyncEx
NuGet包中使用

这是一个使用AsyncContext; 的例子。GeneralThreadAffineContext具有几乎相同的用法:

using Nito.AsyncEx;
class Program
{
    static void Main(string[] args)
    {
        AsyncContext.Run(() => MainAsync(args));
    }

    static async void MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

或者,您可以仅阻塞主控制台线程,直到异步工作完成:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

    static async Task MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

注意使用GetAwaiter().GetResult();
这样可以避免AggregateException使用Wait()或时发生的换行Result

更新,2017-11-30: 从Visual Studio 2017更新3(15.3)开始,该语言现在支持async Main-,只要它返回Task或即可Task<T>。因此,您现在可以执行以下操作:

class Program
{
    static async Task Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

语义似乎与GetAwaiter().GetResult()阻塞主线程的样式相同。但是,尚无C#7.1的语言规范,因此这只是一个假设。

2020-05-19