异步调用完成前控制台应用程序终止

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

【讨论】:

这个调用不会避免控制台退出

以上是关于异步调用完成前控制台应用程序终止的主要内容,如果未能解决你的问题,请参考以下文章

同步异步与阻塞非阻塞

AngularJS仅在父控制器完成异步调用时加载控制器

C#_异步编程走进异步编程的世界 - 剖析异步方法(上)

在 iOS 中等待多个网络异步调用

异步 Lambda 函数:返回 promise 或发送 responseURL 不会终止 CloudFormation 自定义资源调用

WCF服务需要调用异步