我是使用 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();
}
}
我知道这不是从"the top."异步运行由于无法在 Main
方法上指定 async
修饰符,如何在 main
中异步运行代码?
15 回答
还没有需要这么多,但是当我使用控制台应用程序进行快速测试并且需要异步时,我刚刚解决了这个问题:
如果您使用的是C#7.1或更高版本,请使用nawfal's answer并将Main方法的返回类型更改为
Task
或Task<int>
. 如果你不是:有
async Task MainAsync
like Johan said .调用其
.GetAwaiter().GetResult()
来捕获基础异常like do0g said .支持取消like Cory said .
第二个
CTRL+C
应立即终止该过程 . (谢谢binki!)句柄
OperationCancelledException
- 返回相应的错误代码 .最终代码如下:
您可以通过执行以下操作而无需外部库来执行此操作:
我将添加一个重要的功能,所有其他答案都忽略了:取消 .
TPL的一大特色是取消支持,控制台应用程序有一种内置取消方法(CTRL C) . 将它们绑定在一起非常简单 . 这就是我构建所有异步控制台应用程序的方式:
正如您所发现的,在VS11中,编译器将禁止使用
async Main
方法 . 在VS2010中使用Async CTP允许(但从未推荐) .我最近有关于async/await和asynchronous console programs的博客文章 . 以下是介绍帖子的一些背景信息:
这就是为什么这是带有
async Main
的控制台程序中的问题:一种解决方案是提供您自己的上下文 - 控制台程序的“主循环”,它是异步兼容的 .
如果您的计算机具有Async CTP,则可以使用My Documents \ Microsoft Visual Studio异步CTP \ Samples(C#Testing)单元测试\ AsyncTestUtilities中的
GeneralThreadAffineContext
. 或者,您可以使用my Nito.AsyncEx NuGet package来自my Nito.AsyncEx NuGet package .这是一个使用
AsyncContext
的例子;GeneralThreadAffineContext
具有几乎相同的用法:或者,您可以阻止主控制台线程,直到您的异步工作完成:
注意使用
GetAwaiter().GetResult()
;这可以避免在使用Wait()
或Result
时发生的AggregateException
包装 .Update, 2017-11-30: 从Visual Studio 2017 Update 3(15.3)开始,该语言现在支持
async Main
- 只要它返回Task
或Task<T>
. 所以你现在可以这样做:语义似乎与阻塞主线程的
GetAwaiter().GetResult()
样式相同 . 但是,C#7.1还没有语言规范,所以这只是一个假设 .当引入C#5 CTP时,你肯定可以用
async
标记Main ...虽然这样做通常不是一个好主意 . 我相信这已经被VS 2013的发布改变成了一个错误 .除非你已经启动了任何其他前台线程,否则当
Main
完成时,你的程序将会退出,即使它已经启动了一些后台工作 .你真的想做什么?请注意,您的
GetList()
方法确实没有添加额外的图层,没有任何实际原因 . 它在逻辑上等同于(但更复杂):要从Main异步调用任务,请使用
用于.NET 4.5的Task.Run()
用于.NET 4.0的Task.Factory.StartNew()(可能需要Microsoft.Bcl.Async库用于异步和等待关键字)
详细信息:http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
在MSDN上,Task.Run Method (Action)的文档提供了此示例,该示例显示了如何从
main
异步运行方法:请注意以下示例后面的语句:
因此,如果您希望任务在主应用程序线程上运行,请参见the answer by @StephenCleary .
关于任务运行的线程,请注意Stephen的comment关于他的回答:
(有关如何合并异常处理以处理
AggregateException
,请参阅Exception Handling (Task Parallel Library) . )最后,在Task.Delay Method (TimeSpan)文档的MSDN上,此示例显示了如何运行返回值的异步任务:
请注意,您可以改为传递一个lambda函数,而不是将
delegate
传递给Task.Run
:在C#7.1中,您将能够执行正确的异步Main .
Main
方法的相应签名已扩展为:对于例如你可能会这样做:
在编译时,异步入口点方法将被转换为调用
GetAwaitor().GetResult()
.详细信息:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main
编辑:
要启用C#7.1语言功能,您需要右键单击项目并单击“属性”,然后转到“构建”选项卡 . 在那里,单击底部的高级按钮:
从语言版本下拉菜单中,选择“7.1”(或任何更高的值):
默认为“最新主要版本”,它将评估(在撰写本文时)C#7.0,它不支持控制台应用程序中的异步主 .
在Main中尝试将对GetList的调用更改为:
为了避免在调用函数某处向某个地方调用函数时出现冻结,该函数尝试重新加入当前线程(卡在等待中),您需要执行以下操作:
(演员只需解决歧义)
在我的情况下,我有一个我希望从我的main方法异步运行的作业列表,已经在 生产环境 中使用了很长一段时间并且工作正常 .
最新版本的C# - C#7.1允许创建异步控制台应用程序 . 要在项目中启用C#7.1,您必须将VS升级到至少15.3,并将C#版本更改为
C# 7.1
或C# latest minor version
. 为此,请转到项目属性 - >构建 - >高级 - >语言版本 .在此之后,以下代码将起作用:
你可以用这个简单的结构解决这个问题:
这将把你所做的一切都放在你想要它的ThreadPool上(所以你开始/等待的其他任务不会尝试重新加入他们不应该的线程),并等到关闭Console应用程序之前完成所有操作 . 不需要特殊循环或外部库 .
编辑:将Andrew的解决方案纳入未捕获的例外 .
C#7.1(使用vs 2017更新3)引入了async main
你可以写:
欲了解更多详情C# 7 Series, Part 2: Async Main
Update:
您可能会收到编译错误:
此错误是由于vs2017.3默认配置为c#7.0而不是c#7.1 .
您应该明确修改项目的设置以设置c#7.1功能 .
您可以通过两种方法设置c#7.1:
Method 1: Using the project settings window:
打开项目的设置
选择“构建”选项卡
单击“高级”按钮
选择所需的版本如下图所示:
Method2: Modify PropertyGroup of .csproj manually
添加此属性:
例: