1

我有一个后台线程,一旦完成就会更新 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() 因为它们都在同一个线程中运行,所以我假设检查对于 IsDisposing 和稍后执行就足够了。但是我怎么能确定 (*) 中的部分不会在两者之间获得 Dispose(),这将导致 BeginInvoke 在处理类上被调用,并最终导致应用程序崩溃?

我通过在 (*) 部分之间添加 Thread.Sleep(2000) 来测试它,在 Thread.Sleep 之前和之后放置断点,并移出控制使其在到达 BeginInvoke 之前获得 Dispose()d。结果 - 我的应用程序崩溃了。我怎么知道运行时不会给我这种不幸的上下文切换场景?

4

4 回答 4

4

它看起来像是一个由 Backgroundworker 免费完全解决的问题。

有什么理由不使用它?

于 2012-04-11T16:35:28.120 回答
2

我将其称为异常情况——在 BeginInvokes 周围使用 try/catch 来捕获并显式处理抛出的异常。

于 2012-04-11T16:34:19.863 回答
0

以下内容在执行时不会显示任何错误消息:

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";
}
于 2012-04-11T21:24:16.583 回答
0

在某些情况下,您可以通过让Dispose控件的例程获取锁、设置标志并释放锁来实现干净关闭;您的更新例程应该获取锁,BeginInvoke如果未设置标志,则执行该操作,然后释放锁。锁的主要目的是确保一旦更新例程决定调用BeginInvoke,在更新发生之前控件不会被释放。

话虽如此,在很多方面,我认为只执行BeginInvoke并吞下如果控件从您的下方处置时将发生的异常会更清洁。确实应该有一个TryBeginInvokewhich 会执行 aBeginInvokeTrue在控件处于活动状态时返回,否则返回False,但遗憾的是,没有,也没有任何无竞争的方式来创建一个,而无需将自己注入控件的Dispose进程或让异常发生并扼杀它。

于 2012-04-11T23:59:16.237 回答