我对使用async修饰符进行异步编程并不熟悉。我试图弄清楚如何确保我的控制台应用程序的主要方法实际上是异步运行的。

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();
    }
}

我知道这不是从“顶层”异步运行的。由于不可能在Main方法上指定异步修饰符,我如何在Main中异步运行代码?


当前回答

class Program
{
     public static EventHandler AsyncHandler;
     static void Main(string[] args)
     {
        AsyncHandler+= async (sender, eventArgs) => { await AsyncMain(); };
        AsyncHandler?.Invoke(null, null);
     }

     private async Task AsyncMain()
     {
        //Your Async Code
     }
}

其他回答

在Main中尝试将GetList调用更改为:

Task.Run(() => bs.GetList());

你可以用这个简单的构造来解决这个问题:

class Program
{
    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            // Do any async anything you need here without worry
        }).GetAwaiter().GetResult();
    }
}

这将把你所做的所有事情放在你想要它的线程池(这样你启动/等待的其他任务就不会试图重新加入一个不应该的线程),并等待所有事情都完成后再关闭控制台应用程序。不需要特殊的循环或外部库。

编辑:合并Andrew的未捕获异常的解决方案。

不确定这是否是你要找的,但我想等待加载方法。我最终使用main_shows处理程序,并使它异步:

private async void Main_Shown(object sender, EventArgs e)
{
   await myAsyncMethod();
}

在MSDN上,Task的文档。Run Method (Action)提供了这个例子,展示了如何从main异步运行一个方法:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        ShowThreadInfo("Application");

        var t = Task.Run(() => ShowThreadInfo("Task") );
        t.Wait();
    }

    static void ShowThreadInfo(String s)
    {
        Console.WriteLine("{0} Thread ID: {1}",
                          s, Thread.CurrentThread.ManagedThreadId);
    }
}
// The example displays the following output:
//       Application thread ID: 1
//       Task thread ID: 3

请注意下面的例子:

这些例子表明异步任务执行在不同的 线程比主应用程序线程多。

因此,如果您希望任务在主应用程序线程上运行,请参阅@StephenCleary的回答。

关于任务运行的线程,也请注意Stephen对他的回答的评论:

您可以使用简单的Wait或Result,没有任何问题 的。但要注意两者之间有两个重要的区别: 所有异步延续都运行在线程池上,而不是主线程池上 2)任何异常都包装在AggregateException中。

(参见异常处理(任务并行库)了解如何合并异常处理来处理AggregateException。)


最后,从MSDN上的任务文档。延迟方法(TimeSpan),这个例子展示了如何运行一个返回值的异步任务:

using System;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        var t = Task.Run(async delegate
                {
                    await Task.Delay(TimeSpan.FromSeconds(1.5));
                    return 42;
                });
        t.Wait();
        Console.WriteLine("Task t Status: {0}, Result: {1}",
                          t.Status, t.Result);
    }
}
// The example displays the following output:
//        Task t Status: RanToCompletion, Result: 42

注意,不是传递一个委托给Task。运行时,你可以像这样传递一个lambda函数:

var t = Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(1.5));
            return 42;
        });

在c# 7.1中,你将能够做一个适当的异步Main。Main方法的适当签名已扩展为:

public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

例如,你可以这样做:

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

在编译时,异步入口点方法将被转换为调用GetAwaitor(). getresult()。

详细信息:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main。

编辑:

要启用c# 7.1语言特性,您需要在项目上右键单击“Properties”,然后转到“Build”选项卡。在那里,点击底部的高级按钮:

从语言版本下拉菜单中,选择“7.1”(或更高的值):

默认是“最新的主要版本”,它将评估(在撰写本文时)c# 7.0,在控制台应用程序中不支持async main。