从后台线程更新 UI - 我怎么知道我的 GUI 未释放
本文关键字:我的 GUI 释放 我怎么知道 后台 线程 更新 UI | 更新日期: 2023-09-27 18:30:58
我有一个后台线程,一旦完成,它就会更新UI。我试图尽可能安全,这样我就不会调用已被处置的 GUI。
void DoInBackground()
{
try
{
string result = ServerSideProcess();
* if (!IsDisposed && !Disposing)
* BeginInvoke(new StringDelegate(UpdateText), result);
}
catch (Exception ex)
{
* if (!IsDisposed && !Disposing)
* BeginInvoke(new VoidDelegate(UpdateFailed));
}
}
void UpdateText(string txt)
{
if (!IsDisposed && !Disposing)
textbox1.Text = txt;
}
void UpdateFailed()
{
if (!IsDisposed && !Disposing)
textbox1.Text = "failed to get data from server";
}
override Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
components.Dispose();
}
base.Dispose(disposing);
}
我认为我在 GUI 方法中足够安全 - 当我在 UpdateText(string) 或 UpdateFailed() 中时不会调用 Dispose(),因为它们都在同一线程中运行,所以我假设检查 IsDispose 并稍后执行就足够了。但是我怎么能确定 (*) 中的部分不会在两者之间获得 Dispose(),这将导致 BeginInvoke 在释放类上被调用,并最终导致应用程序崩溃?
我通过在 (*) 部分之间添加 Thread.Sleep(2000) 来测试它,在 Thread.Sleep 之前和之后放置断点,并移出控件,使其在到达 BeginInvoke 之前获得 Dispose()d。 结果 - 我的应用程序崩溃了。 我怎么知道运行时不会给我这种不幸的上下文切换方案?
它看起来像是一个由后台工作者免费完全解决的问题。
有什么理由不使用它吗?
我称之为特殊条件 - 在 BeginInvokes 周围使用 try/catch 来捕获并显式处理抛出的异常。
执行时,以下内容不会向我显示任何错误消息:
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
Shown += Form1_Shown;
}
void Form1_Shown(object sender, EventArgs e)
{
bw.RunWorkerAsync();
Thread.Sleep(1000);
Dispose();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//This is your second thread.
Thread.Sleep(2000);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//This runs when the second thread is finished. Update here.
Text = "Done";
}
在某些情况下,可以通过让控件的Dispose
例程获取锁、设置标志和释放锁来实现干净关机;更新例程应获取锁,如果未设置标志,则执行BeginInvoke
,然后释放锁。 锁的主要目的是确保一旦更新例程决定调用BeginInvoke
,控件就不会被释放,直到更新发生。
话虽如此,在很多方面,我认为只做BeginInvoke
并吞下如果控制权从你下面处理将发生的异常会更干净。 确实应该有一个TryBeginInvoke
,如果控件处于活动状态,它会执行BeginInvoke
并返回True
,否则返回False
,但可惜没有,也没有任何无种族的方法来创建控件,而无需将自己注入控件的Dispose
过程或让异常发生并扼杀它。