如何避免在控制台应用程序中等待异步操作结束时出现Thread.Sleep(Int32.MaxValue)



我有下面的代码,它将异步下载一个文件到我的硬盘上,向控制台大喊当前的进度,并在最后以再见消息退出:

webClient.DownloadProgressChanged.Add(fun args ->
      if (currentPercentage < args.ProgressPercentage) then
        Console.WriteLine(args.ProgressPercentage.ToString() + "%")
      currentPercentage <- args.ProgressPercentage
  )
webClient.DownloadFileCompleted.Add(fun args ->
  Console.WriteLine("Download finished!")
  Environment.Exit 0
)
webClient.DownloadFileAsync(new Uri(url_to_download),  file_name)
Thread.Sleep Int32.MaxValue

我想知道,是否有更优雅的方式来实现这一点,而不必诉诸主线程中的"永远休眠",让程序通过Environment.Exit()结束。我对使用Environment.Exit()没有偏见,但如果可能的话,我想避免使用它!我能想到的避免这种情况的唯一方法是生成一个新线程,然后等待它死亡,但这似乎很麻烦。有更简单的方法吗?

你可以这样使用ResetEvent:

webClient.DownloadProgressChanged += (f,a) => ...
AutoResetEvent resetEvent = new AutoResetEvent(false);
webClient.DownloadFileCompleted += (f, a) => resetEvent.Set();
webClient.DownloadDataAsync(new Uri(url_to_download), file_name);
resetEvent.WaitOne();
Console.WriteLine("Finished");

只需使用一个等待句柄派生类(如互斥锁)来表示您已准备好关闭。在你的download completed方法中给它发信号,然后在你的应用结束时等待它。当它发出信号时,你的应用将自然退出。

如果你是响应式扩展库(Rx)的粉丝,那么这个过程可以按照如下的可观察对象来建模:

    public static IObservable<int> DownloadURL(string url,string fname)
    {
        return Observable.Defer(() =>
        {
            var sub = new Subject<int>();
            var wc = new WebClient();
            wc.DownloadProgressChanged += delegate(object sender, DownloadProgressChangedEventArgs e)
            {
                sub.OnNext(e.ProgressPercentage);
                if (e.ProgressPercentage == 100)
                    sub.OnCompleted();
            };
            wc.DownloadFileAsync(new Uri(url), fname);
            return sub;
        });
    }
    public static void Main(string[] str)
    {
        foreach (var i in DownloadURL("http://www.google.com", "g:\google.html").DistinctUntilChanged().ToEnumerable())
            Console.WriteLine(i);
    }

在c#中,你可以为WebClient编写一个扩展方法,等待下载完成,同时仍然发送更新事件:

static class WebClientEx {
    public static void DownloadSemiSync(this WebClient webClient, Uri address, string filename) {
        var evt = new AutoResetEvent(false);
        webClient.DownloadFileCompleted += (s, e) => evt.Set();
        webClient.DownloadFileAsync(address, filename);
        evt.WaitOne();
    }
}

这将允许您定义任何您想要的进度事件,然后将其用作同步函数,将主代码减少为:

    static void Main() {
        var webClient = new WebClient();
        webClient.DownloadProgressChanged += (s, args) => {..};
        webClient.DownloadSemiSync(new Uri("http://.."), "test.bin");
        Console.WriteLine("DownloadFinished");
    }

抛出所有事件,但随后等待退出。

最新更新