【问题标题】:c# cannot-access-a-disposed-object - Form accessed by worker threadc# cannot-access-a-disposed-object - 工作线程访问的表单
【发布时间】:2015-11-27 10:06:33
【问题描述】:

我是一个相对较新的 c# 用户,如果我犯了一些菜鸟错误,请原谅我。我已经寻找解决此错误的方法,但找不到适合我的实现。

我有一个工作线程,它基本上是循环的,而布尔值isCalibrationActive 是真的。在工作线程期间,我设置了一个回调来访问主线程中的标签并根据工作线程更改文本。在主线程中的 formclosure 事件中,我将 isCalibrationActive 设置为 false,理论上应该停止工作线程中的循环。一旦我关闭表单,我就会收到错误消息。

表单关闭事件代码:

private void Form2_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (e.CloseReason == CloseReason.UserClosing)
        {
            DialogResult result = MessageBox.Show("The Calibration stage is not yet complete. Are you sure you want to exit?", "Dialog Title", MessageBoxButtons.YesNo);
            if (result == DialogResult.Yes)
            {
                isCalibrationActive = false;
                Thread.Sleep(10000);
                return;
            }
            else
            {
                e.Cancel = true;
            }
        }
        else
        {
            e.Cancel = true;
        }
    }

工作线程:编辑:抱歉,我之前把测试代码放在那里了。

    private void refreshXY()
    {
        var currentpath = new StringBuilder(255);

        //store current directory and set to dll directory.
        UnsafeNativeMethods.GetDllDirectory(currentpath.Length, currentpath);
        UnsafeNativeMethods.SetDllDirectory( "D:\\UI_still_in_progress\\Debug" );
        UnsafeNativeMethods.LoadLibrary("dllTESTER.dll");

        while (isCalibrationActive)
        {                
            xx = UnsafeNativeMethods.grabx();
            yy = UnsafeNativeMethods.graby();
            if (xx == 0) { xx = 0.001;}
            if (yy == 0) { yy = 0.001;}
            SetXlab(xx.ToString());
            SetYlab(yy.ToString());
        }
        //restore old directory
        UnsafeNativeMethods.SetDllDirectory(currentpath.ToString());
    }

在基本相同的SetXlabSetYlab 函数中出现错误。另外我在另一个线程中使用它的原因是因为它正在从另一个需要持续更新的应用程序中读取实时数据。如下所示:

private void SetYlab(string yval)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.yshow.InvokeRequired)
        {
            SetTextCallback yD = new SetTextCallback(SetYlab);
            if (isCalibrationActive) { this.Invoke(yD, new object[] { yval }); }
        }
        else
        {
            this.yshow.Text = yval;
        }
    }

具体而言,this.Invoke(yD, new object[] { yval }); } 行是导致错误的原因。有什么办法可以避免这种情况吗?

【问题讨论】:

  • grabxgraby 是合理快速运行的方法吗?像一般少于 5 毫秒,很少超过 50 毫秒,而且永远不会超过一秒?
  • (和菜鸟的错误——C#“de-facto”编码标准使用 PascalCase 作为方法和属性,仅供参考;随意做你想做的事)

标签: c# multithreading


【解决方案1】:

我可能会使用一个名为 calGuiUpdateTimerSystem.Windows.Forms (SWF) Timer,以及类似这样的 Tick 函数

        xx = 0;
        yy = 100;
        xx = UnsafeNativeMethods.grabx();
        yy = UnsafeNativeMethods.graby();
        if (xx == 0) { xx = 0.001;}
        if (yy == 0) { yy = 0.001;}
        this.xshow.Text = xx.ToString();
        this.yshow.Text = yy.ToString();

然后去掉isCalibrationActive,用calGuiUpdateTimer.Enabled代替它。

SWF 计时器始终在 GUI 线程上运行,因此您完全不必担心切换线程;您可以完全摆脱所有这些 InvokeRequired 垃圾的功能。让您的工作更轻松。

也就是说,如果 grabxgraby 可能是运行缓慢的函数,则此选项会使您的 GUI 卡顿,并且 SWF 计时器将是不可接受的。在这种情况下,请继续使用您的后台线程解决方案并查看 Avoid calling Invoke when the control is disposed 以获取指导(另请参阅 https://stackoverflow.com/a/661662/171121 以简化您的回调)。

【讨论】:

  • 谢谢,我意识到我可以让工作线程更新全局变量,并让 timer.tick 事件更新表单:)
猜你喜欢
  • 2023-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多