0

バックグラウンド

ここで受け取った貴重なアドバイスから、データベースを集中的に使用するすべてのコードをバックグラウンドワーカーに移動しました。具体的には、データベースへの直接呼び出しです。そのコードは、backgroundworkerのDoWorkイベント中に実行されます。DoWorkイベント中にDataTableが返される場合は、そのDataTableをクラス全体の変数に設定します。これは、このコードを実行するたびにDataTableを必要とするコントロールを呼び出す必要がないようにするために行われます。

そのコードが実行されている間、何かが起こっていることをユーザーに知らせるために、メインUIスレッドで更新されるラベルがあります。ラベルを更新するには、750ミリ秒ごとに「。」となるようにタイマーを使用します。ラベルの文字列に追加されます。

私が最初に気付いたのは、backgroundworkerのRunWorkerCompletedイベントがトリガーされていないことでした。これを解決するためApplication.DoEvents();に、バックグラウンドワーカーに電話をかける前に行いました。それは醜いものでしたが、それがイベントを引き起こしました。誰かがこれを修正するための代替手段を持っているなら、私はすべての耳です。

それから私は興味深い苦境に出くわしました。Visual Studio 2010内でプログラムをデバッグモードで実行すると、「クロススレッド操作が無効です:作成されたスレッド以外のスレッドからアクセスされたコントロール'lblStatus'」というInvalidOperationExceptionエラーが発生します。このエラーは、バックグラウンドワーカーの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

while()UIスレッドでそのようなループを実行しないでください。
ループが終了するまでUIをフリーズしています。これは目的を打ち負かします。

さらにSystem.Timers.Timer、UIスレッドでコールバックを実行しません。
代わりにWinFormsタイマーを使用してください。

WinFormsタイマーに切り替えると、タイマーコールバック内のラベルに追加するだけで、操作が終了したときにタイマーを無効にできます。

于 2012-08-21T20:27:58.370 に答える