110,534
社区成员
发帖
与我相关
我的任务
分享
private void button4_Click(object sender, EventArgs e)
{
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync("Tank");
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// 这里是后台线程, 是在另一个线程上完成的
// 这里是真正做事的工作线程
// 可以在这里做一些费时的,复杂的操作
Thread.Sleep(5000);
e.Result = e.Argument + "工作线程完成";
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了
this.label4.Text = e.Result.ToString();
}
using (BackgroundWorker bw = new BackgroundWorker())
{
.....
} //将子窗体完全关闭
this.Close();
this.Dispose();
this.Close();
this.Dispose();
[/code]this.Close();
this.Dispose();
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
但是下面两个例子,出了问题
BeginInvoke(new MethodInvoker(
() =>
{
Thread.Sleep(5000);//这里没有运行,是否因为窗体已经释放了,所以此线程也被停止了
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
}));
this.Close();
this.Dispose();
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
this.Close();
this.Dispose();
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
BeginInvoke(new MethodInvoker( //程序执行到此处,发生了异常- -
//异常信息是:在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。
() =>
{
Thread.Sleep(5000);
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
}));
看来确实是BackgroundWorker的方法,对跨线程调用控件进行了保护,所以即使窗体执行了dispose也依然未完全释放,这也是原文作者对BackgroundWorker跨线程调用控件的强烈推荐的原因吧using (BackgroundWorker bw = new BackgroundWorker())
{
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync(argument);
}
this.Close();
this.Dispose();
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
原测试代码,已更改为正常的代码,这仅算是个模拟的代码
bw_DoWork //后台执行的代码
{
thread.sleep(5000)//这里加了断点,此处确实休眠了5秒
}
bw_RunWorkerCompleted//完成后台后,返回主线程(子窗体内)的代码
{
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
}
此段代码,什么问题也没有,全部正常执行,并且 this.textBox_LoginName.Text 也被正确赋值,(不过我没试在父窗体内,再读取textBox_LoginName.Text 会发生什么,理论上当然会非法访问,所以没有去测试)
BackgroundWorker正常执行了,并且与BeginInvoke(new MethodInvoker(...)对比,很明显,BackgroundWorker执行时对子窗体资源的释放进行了保护, 查看我提的3个例子的最后一个例子的异常即可知道 ex.tostring()=在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。BeginInvoke(new MethodInvoker(//此段代码替换为BackgroundWorker的,让其在后台执行相关跨线程调用控件
() =>
{
Thread.Sleep(5000);//这里没有运行,是否因为窗体已经释放了,所以此线程也被停止了
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
}));
this.Close();
this.Dispose();
GC.Collect();
this.Close();
this.Dispose();
this.textBox_LoginName.Text = "aa";
(源代码已经全部修正成正常的逻辑代码了,所以未贴详细的测试代码)
如上更改代码后,则不会出现异常,而且我跟踪代码,各处代码均有正确的值,而不是null,或者说没有被GC掉,(并且我还在BackgroundWorker后台执行委托里Sleep了5000毫秒),也就是说子窗口的这个this在BackgroundWorker的相关执行中,主线程(子窗体)一直没有被释放,或许就是因为有了强引用,.net保护了子窗体,并没在其GC dispose后释放,(这按C++的思路,是该出错的,并且调用BeginInvoke(new MethodInvoker(...)也确实出错了,但是BackgroundWorker没有出错,我疑惑或者说惧怕的原因就是在这里)