异步调用完成前控制台应用程序终止
Posted
技术标签:
【中文标题】异步调用完成前控制台应用程序终止【英文标题】:Console App Terminating Before async Call Completion 【发布时间】:2011-04-19 22:34:22 【问题描述】:我目前正在编写一个 C# 控制台应用程序,它生成许多指向网站上不同图像的 URL,然后使用 WebClient.DownloadDataAsync()
作为字节流下载。
我的问题是,一旦进行第一次异步调用,控制台应用程序会认为程序已完成并在异步调用返回之前终止。通过使用Console.Read()
,我可以强制控制台保持打开状态,但这似乎不是很好的设计。此外,如果用户在此过程中(在控制台等待输入时)按 Enter 键,程序将终止。
在我等待异步调用返回时,有没有更好的方法来防止控制台关闭?
编辑:调用是异步的,因为我在下载时通过控制台向用户提供状态指示器。
【问题讨论】:
【参考方案1】:是的。使用ManualResetEvent,并进行异步回调调用event.Set()
。如果 Main 例程在 event.WaitOne()
上阻塞,它将在异步代码完成之前退出。
基本的伪代码如下所示:
static ManualResetEvent resetEvent = new ManualResetEvent(false);
static void Main()
CallAsyncMethod();
resetEvent.WaitOne(); // Blocks until "set"
void DownloadDataCallback()
// Do your processing on completion...
resetEvent.Set(); // Allow the program to exit
【讨论】:
谢谢!这是有道理的,而且效果很好……比 console.read 好多了! 我还发现这在使用长时间运行的Task
作为主要工作人员来调试 Windows 服务时很有用。在Program.Main()
中,使用像#if DEBUG [debug hook] #else [normal service code] #endif
这样的预处理器指令然后在“调试钩子”中,我只是在new MyService().OnStart()
之后添加对resetEvent.WaitOne()
的调用。然后您可以使用 VS debug 在调试模式下持续运行,而无需以任何有意义的方式修改您的实际服务代码。
在 OP 的特定情况下它可能会起作用,但通常如果后台任务在主线程上安排某些内容并等待它完成,这会导致死锁。只是一个小警告。【参考方案2】:
另一种选择是调用异步 Main 方法并等待返回的任务。
static void Main(string[] args)
MainAsync(args).Wait();
static async Task MainAsync(string[] args)
// .. code goes here
从 .NET 4.5 开始,您现在可以调用 GetAwaiter().GetResult()
static void Main(string[] args)
MainAsync(args).GetAwaiter().GetResult();
What is the difference between .Wait() vs .GetAwaiter().GetResult()?
【讨论】:
漂亮又简单,正是我想要的。【参考方案3】:如果这就是您的应用程序正在做的所有事情,我建议您省略异步调用;您特别希望应用程序在下载完成之前“挂起”,因此使用异步操作与您在这里想要的相反,尤其是在控制台应用程序中。
【讨论】:
我在问题解释中忽略了这一点,但在下载过程中,我在控制台窗口中显示了一个进度指示器。我们正在谈论成千上万的图像,因此最终用户了解正在发生的事情将是有价值的。 没关系;您仍然可以从同一个线程更新控制台;用户除了 Control-C 之外什么都做不了,但无论如何,这就是您要寻找的行为。 如果您需要扩展它以下载多个文件等,使用 async 也可能是有益的...不确定这里的基本原理,但如果它只是一个文件,同步会更容易.. @Reed 我应该更清楚我假设他只做一个文件,是的。谢谢,里德! +1【参考方案4】:需要说明的是,在 C# 7.1 中,Main
现在可以标记为async
。
static async Task Main()
await LongRunningOperation();
【讨论】:
这个调用不会避免控制台退出以上是关于异步调用完成前控制台应用程序终止的主要内容,如果未能解决你的问题,请参考以下文章
异步 Lambda 函数:返回 promise 或发送 responseURL 不会终止 CloudFormation 自定义资源调用