繁体   English   中英

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

[英]How to avoid having a Thread.Sleep(Int32.MaxValue) when waiting for an asynchronous operation to end in a Console app?

我有以下代码,这些代码将异步将文件下载到硬盘驱动器,向控制台大喊当前进度,最后退出并显示再见消息:

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

只需使用诸如互斥锁之类的waithandle派生类即可表明您已准备好关闭。 在您的下载完成方法中发出信号,并在应用程序结束时等待它。 当它发出信号时,您的应用将自然退出。

如果您是Reactive Extensions Library(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");
    }

引发所有事件,然后等待退出。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM