【问题标题】:Console App Terminating Before async Call Completion异步调用完成前控制台应用程序终止
【发布时间】:2011-04-19 22:34:22
【问题描述】:

我目前正在编写一个 C# 控制台应用程序,它生成许多指向网站上不同图像的 URL,然后使用 WebClient.DownloadDataAsync() 作为字节流下载。

我的问题是,一旦进行第一次异步调用,控制台应用程序会认为程序已完成并在异步调用返回之前终止。通过使用Console.Read(),我可以强制控制台保持打开状态,但这似乎不是很好的设计。此外,如果用户在此过程中(在控制台等待输入时)按 Enter 键,程序将终止。

在我等待异步调用返回时,有没有更好的方法来防止控制台关闭?

编辑:调用是异步的,因为我在下载时通过控制台向用户提供状态指示器。

【问题讨论】:

    标签: c# asynchronous webclient windows-console


    【解决方案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();
    }
    

    【讨论】:

    • 这个调用不会避免控制台退出
    猜你喜欢
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多