0

背景

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

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

我注意到的第一件事是 backgroundworker 的 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!
        }
    }
}
4

1 回答 1

0

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

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

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

于 2012-08-21T20:27:58.370 回答