5

ReportProgressを介して定期的にGUIを更新するバックグラウンドワーカーがいます。

更新は定期的に、たとえば5秒ごとに発生しますが、20秒になることもあります。設定された時間に更新を実行するために、ワーカープロセスをスリープ状態に送信し、スリープ状態になると、GUIを新しい情報で更新します。

ワーカーはキャンセルをサポートし、スリープ以外では正しくキャンセルします。

待機期間中にキャンセルを呼び出せるようにしたいのですが、スレッドをスリープ状態にすると、これは不可能になります。

スレッドのスリープをシミュレートするには、ループを呼び出して、ループの一部としてキャンセルを確認する必要があると思います。

これを達成するための最良の方法は何ですか、私のタイミングは私の試みとは完全にずれています。

            long counter = 0;
            long sleepfor = timelinespeed*1000;
            int timelinespeed = 10;

                while (counter != sleepfor)
                {

                    Thread.Sleep(1);

                    counter++;

                    if (bkgwk.CancellationPending)
                    {
                        cancelled = true;
                        e.Cancel = true;
                        bkgwk.Dispose();
                        break;
                    }

                }
4

3 に答える 3

16

あなたは間違った道を進んでいます。

  • BgwはThreadPoolを使用しているため、(比較的)短いアクション(通常は0.5秒未満)に使用する必要があります。1つを無期限に保持することはお勧めしません。
  • ほとんどSleep()のスレッドで使用するべきではありません。確かに数ミリ秒以上ではありません。
  • Sleep(1)ビジーウェイトで5秒間使用することは、CPUの大きな浪費です。あなたは他のスレッドを傷つけています。少なくとも、それを約に上げることを検討してくださいSleep(100)。キャンセルに許可する最大遅延を見つけます。

解決策として:タイマーを使用します。定期的なタスクがあります。適切なツールを使用してください。

System.Threading.TimerThreadPoolベースのバックグラウンド作業に使用します。Dispatcher.Invoke()を使用してGUIを更新します。

を使用しDispatcher.Timerて、メインスレッドで作業の小さな部分(0.1秒未満)を実行します。GUIを直接更新できます。

于 2012-04-23T20:25:58.567 に答える
2

ManualResetEventを使用して、スレッドがポーリングせずに待機できるようにすることと、別のスレッドがいつでもスレッドをウェイクアップできるようにすることができます。

ManualResetEvent他のスレッド同期オブジェクトと同様にWaitOne、イベントが設定されるのを待つか、タイムアウトするのを待つことができるメソッドがあります。したがって、通常はWaitOne、待機したい量(のように機能しますThread.Sleep)を待機することができますが、メインスレッドはいつでもバックグラウンドスレッドをウェイクアップできます。

ManualResetEvent backgroundWakeEvent = new ManualResetEvent(false);
....
bkgwk.CancelAync();
backgroundWaitEvent.Set();
....
backgroundWakeEvent.WaitOne(5000);

if (bkgwk.CancellationPending)
{
    cancelled = true;
    e.Cancel = true;
    bkgwk.Dispose();
    break;
}
于 2012-04-23T20:28:46.737 に答える
2

またはさらに良いことに、Reactive Extensionsフレームワークを使用して、「ワーカー」にインターバルイベントのストリームを監視してもらいます。その後、ストリームを簡単に破棄して、新しいイベントの生成を停止し、ワーカーを終了させることができます。これをキャンセルディスポーザブルと組み合わせると、インターバルタイマーとその作業を行うすべてのワーカーの両方を停止する1つのディスポーザブルがあります。

var cancel = new BooleanDisposable();
var subscription = Observable.Interval(TimeSpan.FromSeconds(20))
                             .ObserveOn(Scheduler.DispatcherScheduler)
                             .Subscribe(i => PerformWorkOnUI(cancel));
var uiWork = new CompositeDisposable(cancel, subscription);

タイマーイベントのストリームをキャンセルするには、複合キャンセル/サブスクリプションを破棄するだけです。

uiWork.Dispose();

明確化: ObserveOn(Scheduler.DispatcherScheduler)サブスクリプションがディスパッチャースレッドで呼び出されるようにします。

于 2012-04-23T20:56:15.583 に答える