【问题标题】:C# backgroundworkers + timer: crossthreading issueC# backgroundworkers + timer:跨线程问题
【发布时间】:2012-08-21 20:25:54
【问题描述】:

背景

根据here 收到的宝贵建议,我现在已将所有数据库密集型代码移至后台工作程序,特别是对数据库的直接调用。该代码在后台工作人员的 DoWork 事件期间执行。如果在 DoWork 事件期间返回 DataTable,我将该 DataTable 设置为类范围的变量。这样做是为了避免每次运行此代码时都必须调用需要 DataTable 的控件。

在执行该代码时,我在主 UI 线程中更新了一个标签,以让用户知道正在发生某些事情。为了更新标签,我使用了一个计时器,例如每 750 毫秒一个“。”被附加到标签的字符串中。

我注意到的第一件事是后台工作人员的 RunWorkerCompleted 事件没有触发。为了解决这个问题,我在每次打电话给后台工作人员之前都做了一个Application.DoEvents();。这很丑陋,但它导致了事件的触发。如果有人可以解决此问题,我会全力以赴。

然后我遇到了一个有趣的困境。如果我在 Visual Studio 2010 中运行程序,在调试模式下,我会收到一个 InvalidOperationException 错误,指出“跨线程操作无效:控制 'lblStatus' 从创建它的线程以外的线程访问”。在后台工作人员的 RunWorkerCompleted 事件期间发生此错误,我在主 UI 线程中设置标签的文本。但是,当我通过可执行文件直接启动应用程序时,它完全可以按预期工作(即标签的文本设置正确)。

问题

谁能解释发生了什么/就如何改进这一点提供建议?

代码

我不能发布所有涉及的代码,但这里有一些相关的东西:

namespace Test
{
    public partial class frmMain : Form
    {
        public static Boolean bStatus = false;
        static Boolean bTimer = false;
        System.Timers.Timer MyTimer = new System.Timers.Timer();

        public frmMain()
        {
            InitializeComponent();
            MyTimer.Elapsed += new System.Timers.ElapsedEventHandler(MyTimer_Elapsed);
            MyTimer.Interval = 750; // Every 3/4 of a second
            ExampleTrigger();
        }

        /// <Insert>Lots of unshown code here</Insert>

        private void ExampleTrigger()
        {
            // This is used to simulate an event that would require the backgroundworker
            Application.DoEvents();
            bgw.RunWorkerAsync(0);
            WaitText("Example - 1");
        }

        private static void MyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            bTimer = true;
        }

        // Update status text
        private void WaitText(string txt)
        {
            MyTimer.Enabled = true;
            lblStatus.Text = txt;
            bStatus = false;
            while (!bStatus)
            {
                if (bTimer)
                {
                    txt = txt + ".";
                    lblStatus.Text = txt;
                    lblStatus.Update();
                    bTimer = false;
                }
            }
            MyTimer.Enabled = false;
        }

        private void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            int iSelect = (int)e.Argument;
            switch (iSelect)
            {
                case 0:
                    // Hit the database
                    break;
                /// <Insert>Other cases here</Insert>
                default:
                    // Do something magical!
                    break;
            }
        }
        private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bStatus = true;
            lblStatus.Text = "Ready!";  // This is where the exception occurs!
        }
    }
}

【问题讨论】:

    标签: c# exception backgroundworker multithreading


    【解决方案1】:

    切勿在 UI 线程中像这样运行 while() 循环。
    您正在冻结 UI,直到循环终止;这违背了目的。

    此外,System.Timers.Timer 不在 UI 线程中运行回调。
    请改用 WinForms 计时器。

    切换到 WinForms 计时器后,您可以简单地附加到计时器回调中的标签,并在操作完成时禁用计时器。

    【讨论】:

    • 谢谢!我使用 WinForms Timer 设置了一个新项目,并弄清楚了一些事情。我也不再使用 while() 循环。我将使用这个起点来修改我的实际程序。
    猜你喜欢
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2013-09-24
    • 1970-01-01
    相关资源
    最近更新 更多