2

私のアプリには、ピクチャボックスと2つのボタン(「はい」と「いいえ」)があります。はいは結果リストに 1 を追加し、いいえは 0 を追加して、両方とも次の画像に進みます。ここで、アプリにタイマーを実装して、答えが提供されない場合に画像を次に移動する必要があります。バックグラウンドワーカーを使用することを考えました。

以下のコードは、ボタンをクリックしないと写真をうまく切り替えます。ボタンをクリックすると、バックグラウンドワーカーが「ビジー」のままになるため、UI がフリーズします。CancelAsync がバックグラウンドワーカーをすぐに停止しないことは理解していますが、DoWork の return-statement が実際にヒットしています。

ここでの私の質問は、なぜバックグラウンドワーカーが忙しくしているのか、それとも私がここで完全に間違った方向に進んでいるのかということです.

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Counter = 0;

        _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.DoWork += _backgroundWorker_DoWork;
        _backgroundWorker.WorkerSupportsCancellation = true;
        _backgroundWorker.RunWorkerAsync();
    }

    private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bgw = sender as BackgroundWorker;

        GoToNextItem(); //Show next picture
        while (!bgw.CancellationPending) 
        {
            _getNext = false;
            Stopwatch sw = Stopwatch.StartNew();

            //Wait interval-time
            while (!_getNext)
            {
                 if ((sw.ElapsedMilliseconds > Test.Interval * 1000) && !bgw.CancellationPending)
                {
                    _getNext = true;
                }
                if (bgw.CancellationPending)
                {
                    e.Cancel = true;
                    return; //Breakpoint is hit here
                }
            }
            if (_getNext)
            {
               Result.Add(0);
                GoToNextItem();
            }
        }
        e.Cancel = true;
    }

    private void btnNo_Click(object sender, EventArgs e)
    {
        _backgroundWorker.CancelAsync();
        Result.Add(0);

        while (_backgroundWorker.IsBusy)
        {
        _backgroundWorker.CancelAsync();
            System.Threading.Thread.Sleep(20);
        }
        _backgroundWorker.RunWorkerAsync();
    }

    private void btnYes_Click(object sender, EventArgs e)
    {
        _backgroundWorker.CancelAsync();
        Result.Add(1);

        while (_backgroundWorker.IsBusy) //Stays busy ==> UI freezes here
        {
            _backgroundWorker.CancelAsync();
            System.Threading.Thread.Sleep(20);
        }
        _backgroundWorker.RunWorkerAsync();
    }

編集

@Servy の提案に従ってタイマーを使用してコードを変更しました。backgroundworker-question の詳細については、受け入れられた回答のコメントをお読みください。

4

3 に答える 3

3

これには a を使用するだけSystem.Windows.Forms.Timerです。

private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
    InitializeComponent();

    timer.Interval = 5000;
    timer.Tick += timer_Tick;
    timer.Start();
}

private void timer_Tick(object sender, EventArgs e)
{
    //runs in UI thread; code to go to next picture goes here
}

private void btnYes_Click(object sender, EventArgs e)
{
    timer.Start();
}
private void btnNo_Click(object sender, EventArgs e)
{
    timer.Start();
}

yes ボタンと no ボタンのクリックも呼び出す必要がStartあります。タイマーがリセットされるためです。これにより、次の画像のカウントダウンの開始に戻ることができます。

Stop発射を停止したいときはいつでも呼び出すことができます。

現在のコードで UI がフリーズする理由は、クリック イベント ハンドラーが UI スレッドで実行さSleepれ、バックグラウンド ワーカーを待機している間に呼び出しているためです。そのアプローチを修正しようとすることはお勧めしません。タイマーを使用する必要がありますが、必要に応じて、バックグラウンドワーカーの完了/キャンセルイベントにイベントハンドラーをアタッチし、現在行っていることをすべて実行する必要がありますUIスレッドで待機するのではなく、他のハンドラーで「BGWの準備が整うまで待機」します。

于 2012-10-09T16:13:00.047 に答える
1

ボタンをクリックすると、バックグラウンド ワーカーにキャンセルするように指示されますが、スレッド スリープもすぐに開始されます。これでは、基本的に、バックグラウンド ワーカーが効果的にキャンセルするのに十分な時間がありません。

私はあなたのアプローチをテストし、Application.DoEvents()次のように行を追加しました:

while (_backgroundWorker.IsBusy)
{
    _backgroundWorker.CancelAsync();
    Application.DoEvents();
    System.Threading.Thread.Sleep(20);
}

これにより、バックグラウンド ワーカーはより迅速にキャンセルできますがApplication.DoEvents()、この方法で を使用するのはコードの匂いだと思います。@Servy が提案する方法を使用することをお勧めします。

于 2012-10-09T16:21:12.120 に答える
1

バックグラウンド ワーカーの使用方法が間違っていると思います..UI をハングさせずに、バックグラウンドで実行する必要があることを行う必要があります..

しかし、あなたの場合、そのような要件はありません..

同じことを行うためにタイマーコントロールを使用し、GoToNextItemメソッドでYES / NOボタンを無効にして、最後に有効に戻してください..

于 2012-10-09T16:27:36.363 に答える