c# - 线程仍在尝试访问已处理的表单?
问题描述
基本上,我有一个线程可以下载并将下载状态报告到进度条和标签。即使之前调用了 a ,它在尝试以已处置形式(进度条和标签)调用对象时总是崩溃,并且即使在同一个 try 块中调用if (!this.Disposed)
a 仍然会引发异常。catch (ObjectDisposedException)
我不确定我能做些什么来解决这个问题,最好将它描述为我遇到过的最烦人的事情。
谢谢。
更新(来自一个体贴的 SO 潜伏者)我在 pastebin 上找到的来源
Thread downloader();
public bool abortThread = false();
private void frmDownload_FormClosing(object sender, FormClosingEventArgs e) {
downloader.Abort(); // Abort the thread before closing the form...?
abortThread = true; // Set the abortThread to true
this.Dispose(); // Dispose thread
}
downloader = new Thread(() => {
string[] URLs = { "http://test1.com/", "http://test2.com/", "http://test3.com/" };
try {
using (WebClient wc = new WebClient()) {
wc.DownloadProgressChanged += (s, e) => {
if (!pbDownloadStatus.IsDisposed && !lbPercentage.IsDisposed) {
if (!abortThread) {
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value = e.ProgressPercentage)); // EXCEPTION HAPPENS HERE
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value++));
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value--));
this.Invoke((MethodInvoker)(() => lbPercentage.Text = e.ProgressPercentage.ToString() + "%"));
}
}
};
wc.DownloadFileCompleted += (s, e) => {
if (!pbDownloadStatus.IsDisposed && !lbPercentage.IsDisposed) {
lock (e.UserState) {
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value = 0));
this.Invoke((MethodInvoker)(() => lbPercentage.Text = "0%"));
Monitor.Pulse(e.UserState);
}
}
};
wc.Proxy = WebProxy.GetDefaultProxy();
wc.Headers.Add(header);
for (int i = 0; i < URLs.Length; i++) {
var sync = new Object();
lock (sync) {
wc.DownloadFileAsycn(new Uri(URLs[i]), "C:\Test\URL" + i);
Monitor.Wait(sync);
}
}
}
}
}
catch (ObjectDisposedException disEx) { // Never gets caught
downloader.Abort();
MessageBox.Show("Object was disposed");
}
});
downloader.Start();
解决方案
从 .NET Framework 4 开始,使用 System.Threading.Tasks.Parallel 和 System.Threading.Tasks.Task 类、Parallel LINQ (PLINQ)、System.Collections.Concurrent 命名空间中的新并发集合类大大简化了多线程编程,以及基于任务概念而非线程概念的新编程模型
在现代,直接使用线程的需求大大减少了,你可能应该看看任务可以取消Tasks
,async/await
更容易管理,异步返回到调用上下文。
其次,你的代码没有意义,而且它充满了编译器错误,这不是一个好的开始。此外,由于代码有很多错误,我决定只给你很多点来考虑反对重写它
因此,让我们看看这段代码存在的一些更明显的问题。
abortThread
从多个线程访问,并且在大多数情况下不是线程安全的。- 你直接打电话
var sync = new Object();
之前lock (sync)
意味着你没有锁定任何东西 - 即使该锁定语句将起作用,也没有其他线程使用该锁定,这意味着它是多余的。
您正在
this.Dispose()
从表单关闭事件中调用。这至少可以说是不寻常的表单未在 Close 上处理的两种情况是: (1) 它是多文档界面 (MDI) 应用程序的一部分,并且表单不可见;(2) 您已经使用 ShowDialog 显示了表单。在这些情况下,您将需要手动调用 Dispose 以将表单的所有控件标记为垃圾回收。
基本上在非 MDI 应用程序中,如果您调用,
ShowDialog
则将其放在 using 语句中。
您正在尝试检查是否
IsDisposed
可以安全地编组回 UI 线程。- 仅仅因为您打电话
Dispose
并不意味着表格已被处置,这不是它的工作方式,也不会解决您的问题。
- 仅仅因为您打电话
- 如果您需要执行异步 IO 绑定工作,请使用
aysnc
,await
模式,那么您不会因为等待完成端口的虚假原因而阻塞线程。如果您需要并行运行它,请考虑使用带有操作块的 DataFlow,以便您可以利用aysnc
,await
和并行。 - 如果您需要确定表单是活动的还是已死的,请使用线程安全变量。使用静态锁定对象,并且在您更新变量的每个地方也使用锁定。
- 如果您需要编组到 UI 线程。不要
this.Invoke((MethodInvoker)(()
多次执行,一次执行,一次更新所有内容。
推荐阅读
- javascript - 在属性匹配的范围(和数据属性)中访问此元素
- java - 获取 span 标签的标题
- sql - 将日期转换为 ISO 星期日期
- kibana - 启动 Kibana 时出错:无法执行二进制文件 - 未定义错误 0
- c# - 为什么统一给我一个错误,说并非所有代码路径都返回一个值
- springdoc - Springdoc-openapi - 有没有办法在 POST 请求中将默认值覆盖为 null 或空?
- java - 顺序 AsyncTasks,其中每个后续使用都来自上一个
- google-apps-script - 通过脚本的 Google 表格计算方法
- php - 如何使用 whereIn 和 take 函数从 laravel 中的 SQL 获取数据?
- java - 战争文件自动化部署