WebClient的DownloadFileCompleted事件触发过早问题排查求助
问题原因及修复方案
1. 事件订阅顺序错误
你在调用webClient.DownloadFileAsync()之后才订阅DownloadProgressChanged和DownloadFileCompleted事件,这会导致程序可能错过初始进度事件,甚至如果下载在订阅完成前就意外终止(比如触发错误),事件处理逻辑根本不会执行。
修复:先订阅事件,再启动异步下载:
webClient = new WebClient(); // 先绑定事件 webClient.DownloadProgressChanged += DownloadProgressChanged; webClient.DownloadFileCompleted += DownloadFileCompleted; // 再开始下载 webClient.DownloadFileAsync(uri, currentFile);
2. 未处理下载异常
DownloadFileCompleted事件触发不代表下载成功,也可能是发生了网络错误、文件权限不足、URI无效等问题。你必须在事件处理方法中检查错误状态:
private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { if (e.Error != null) { if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + $"下载失败: {e.Error.Message}"; fileComplete = true; return; } if (e.Cancelled) { if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + "下载已取消"; fileComplete = true; return; } // 下载成功逻辑 if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + $"下载完成: {currentFile}"; fileComplete = true; }
3. 阻塞UI线程的风险(若此方法在UI线程执行)
用while (!fileComplete) { Thread.Sleep(1200); }等待下载完成,会导致UI线程冻结,WebClient的异步回调因为依赖UI线程执行,会被Sleep阻塞,出现进度不更新、事件触发延迟的问题。
修复:改用async/await替代循环等待,让线程在等待期间能处理UI消息:
private async void DownloadFiles() { foreach (Patch patch in patches) { fileDownloadElapsed.Reset(); fileDownloadElapsed.Start(); CurrentPatch = patch; Uri uri = new Uri(patch.Uri); string uriPath = uri.OriginalString; currentFile = uriPath.Substring(uriPath.LastIndexOf('/') + 1); if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + string.Format("Downloading {0}...", currentFile.ToLower()); using (var webClient = new WebClient()) { webClient.DownloadProgressChanged += DownloadProgressChanged; try { await webClient.DownloadFileTaskAsync(uri, currentFile); if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + $"下载完成: {currentFile}"; } catch (Exception ex) { if (cbDebug.Checked) rtbDebug.Text += Environment.NewLine + $"下载失败: {ex.Message}"; } } } currentFile = string.Empty; updateComplete = true; progbarValue = 0; labelText = "Ready"; }
4. 线程安全问题
fileComplete被多线程访问(主线程循环检查、回调线程赋值),可能出现线程竞争导致逻辑错误。如果坚持用原循环等待方式,需加锁保护变量:
private readonly object _lockObj = new object(); private bool fileComplete; // 回调中赋值 lock (_lockObj) { fileComplete = true; } // 循环中检查 while (true) { lock (_lockObj) { if (fileComplete) break; } Thread.Sleep(100); // 缩短休眠时间,提升检查及时性 }
内容的提问来源于stack exchange,提问作者Robert Weaver
相关产品推荐
相关产品推荐

