[英]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.