如何避免在控制台应用程序中等待异步操作结束时出现Thread.Sleep(Int32.MaxValue)
本文关键字:Thread Sleep MaxValue Int32 结束 控制台 何避免 应用程序 异步操作 等待 | 更新日期: 2023-09-27 18:06:26
我有下面的代码,它将异步下载一个文件到我的硬盘上,向控制台大喊当前的进度,并在最后以再见消息退出:
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");
}
抛出所有事件,但随后等待退出。