C# 从后台线程更新UI-如何知道我的GUI未被释放?

C# 从后台线程更新UI-如何知道我的GUI未被释放?,c#,multithreading,user-interface,dispose,C#,Multithreading,User Interface,Dispose,我有一个后台线程,它会在完成后更新UI。我尽可能地安全,这样我就不会调用已经处理过的GUI void DoInBackground() { try { string result = ServerSideProcess(); * if (!IsDisposed && !Disposing) * BeginInvoke(new StringDelegate(UpdateText), result); }

我有一个后台线程,它会在完成后更新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方法中已经足够安全了——当我在UpdateTextstring或UpdateFiled中时,Dispose不会被调用,因为它们都在同一个线程中运行,所以我假设检查IsDisposing和以后执行就足够了。但是我如何才能确定*中的部分不会在中间得到Dispose,这将导致BeginInvoke在disposing类上被调用,最终导致应用程序崩溃


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

我会称之为异常条件-在BeginInvokes周围使用try/catch来捕获并显式处理抛出的异常。

我会称之为异常条件-在BeginInvokes周围使用try/catch来捕获并显式处理抛出的异常处理抛出的异常。

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


有什么理由不使用它吗?

这看起来像是一个完全由后台工作人员免费解决的问题


有没有不使用它的原因?

执行以下命令时,不会显示任何错误消息:

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";
}

执行以下命令时,不会显示任何错误消息:

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,如果控件处于活动状态,它将开始激活并返回True,否则返回False,但遗憾的是,没有,也没有任何无种族限制的方法来创建一个控件,而无需将自己注入控件的Dispose进程,或者让异常发生并扼杀它。

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


话虽如此,在很多方面,我认为,如果控件在您的控制下被处理掉,那么只做BeginInvoke并吞下将发生的异常会更干净。确实应该有一个TryBeginInvoke,如果控件处于活动状态,它将开始调用并返回True,否则返回False,但可惜没有,如果不将自己注入控件的Dispose进程,或者不让异常发生并扼杀它,也没有任何无种族歧视的方法来创建一个线程池。

BackgroundWorker使用线程池线程,不是吗?有些任务最好使用自己的线程,因为它们会花费大量时间进行阻塞,或者因为它们使用ThreadStatic数据,这些数据在完成任务后应该会消失。从池中“窃取”一个线程是可以接受的。无论如何都应该避免ThreadStatic/ThreadLocal。如果一个线程从池中被盗并保留很长时间,这不会影响任何事情,但是1在一个线程成为问题之前,有多少事情可以占用它,2如果池恰好被备份,则对线程的请求可能会被任意延迟。至于ThreadStatic,它并不美妙,但在某些情况下,它可能是最不邪恶的做事方式。懒惰是代码有点陈旧的主要原因。我不认为它能解决我的问题,但它确实解决了。谢谢BackgroundWorker使用线程池线程,不是吗?有些任务最好使用自己的线程,因为它们会花费大量时间进行阻塞,或者因为它们使用ThreadStatic数据,这些数据在完成任务后应该会消失。从池中“窃取”一个线程是可以接受的。无论如何都应该避免ThreadStatic/ThreadLocal。如果一个线程从池中被盗并保留很长时间,这不会影响任何事情,但是1在一个线程成为问题之前,有多少事情可以占用它,2如果池恰好被备份,那么一个人对线程的请求可能是武断的
我迟到了。至于ThreadStatic,它并不美妙,但在某些情况下,它可能是最不邪恶的做事方式。懒惰是代码有点陈旧的主要原因。我不认为它能解决我的问题,但它确实解决了。谢谢在InvokeRequired、BeginInvoke等的实现方式中存在一些不幸的怪癖,例如,如果您不能在Dispose例程中设置锁保护标志,则没有无种族竞争的方式来执行BeginInvokeUnlessWindowIsGone。“你只需要做一个开始,然后吞下例外情况。”supercat同意,这就是为什么我认为这是一个例外情况,所以try/catch是合理的。从呼叫方的角度来看,是的。这是一个很小的设计,但臭味来自于框架未能认识到许多初始事件和事件的发生是为了目标的利益,而不是为了调用方的利益;类似的臭味也会发生在事件中,例如,因为没有WeakEvent或WeakDelegate。在InvokeRequest、BeginInvoke等的实现方式中存在一些不幸的怪癖,因此,如果您无法在Dispose例程中设置锁保护标志,则无法以无种族竞争的方式执行BeginInvokeUnlessWindowIsGone。“你只需要做一个开始,然后吞下例外情况。”supercat同意,这就是为什么我认为这是一个例外情况,所以try/catch是合理的。从呼叫方的角度来看,是的。这是一个很小的设计,但臭味来自于框架未能认识到许多初始事件和事件的发生是为了目标的利益,而不是为了调用方的利益;类似的臭味也会发生在事件中,例如,因为没有弱事件或弱事件。