2011-06-14 13 views
3

私は過去数ヶ月間スレッドで遊んでいましたが、私の出力は期待通りですが、これは最善の方法ではないと感じています。私はベストプラクティスと一緒に仕事をしている人からまっすぐな答えを得ることができないので、私はあなたに頼むと思った。C#WPF正しく停止する方法をスレッド

質問:私はこの単純なので、私に耐えるようにしようとします。 startとstopボタンがあるフォームがあるとします。開始ボタンが起動し、スレッドを開始するイベント。このスレッドのDoWorkの中には、3つのメソッドが呼び出されます。 Method1()はコンソール "A \ n"に10秒の間隔で10秒間印刷します。 Method2()とMethod3()は全く同じ文字で、異なる休止時間はConsole.WriteLineです。ここで停止ボタンを押すと、応答が即時になります。私は方法が完了するのを待つ必要はありません。これについてどうすればいいですか?

私は、これは各メソッドに私のBackgroundWorkerを渡すと、私はしかし、法1は、より多くなっていることを想像し、これが私の望ましい結果が得られたようなので、

public void Method1(BackgroundWorker worker) 
    { 
     for(int i = 0; i < 10 && !worker.CancellationPending; ++i) 
     { 
     Console.WriteLine("A"); 
     for(int j = 0; j < 100 && !worker.CancellationPending; ++i) 
     { 
      Thread.Sleep(100); 
     } 
     } 
    } 

ようworker.CancellationPendingをチェックしてやっている方法複雑な、それはkeydownとキーを持っている書き込みにDLLを使用しているとしよう。私がスレッドを中断しただけでは、自分自身も望ましくない状態に陥る可能性があります。私は!worker.CancellationPendingで自分のコードを散らしています。実際にはすべてのコードブロック私はCancellationPendingをチェックしています。私は多くの例を見ていて、まるで私のようにスレッドを周回している人はほとんどいません。これに関するベストプラクティスは何ですか?

+1

(それは操作とどのようにそうするようにを中止しても安全だときのみ、あなたが知っているので)あなたは本当に取り消し要求をチェック周りを取得することはできませんが、.NET 4.0を使用している場合、 TPLと新しいキャンセルシステムをチェックしてください。 CancellationTokenの配布を許可することにより、Task(またはBackgroundWorker)からのキャンセルを抽象化します。これにより、キャンセルを実行できるユーザー(CancellationTokenSourceの所有者のみがこれを行うことができます)と、単にキャンセル要求を聞いているユーザーを分けることもできます。 –

+0

"immediate"は相対的です。 「取り消しアクション」メッセージを表示することは、ユーザに同じ期待を満たすかもしれない。 – StingyJack

答えて

1

ステップを分割するためにイテレータ(yield yield)を使用することを検討してください。

public void Method1(Backgroundworker worker) 
    { 
    foreach (var discard in Method1Steps) 
    { 
     if (worker.CancelationPending) 
      return; 
    } 
    } 

    private IEnumerable<object> Method1Steps() 
    { 
    for (int i = 0; i < 10; ++i) 
    { 
     yield return null; 
     Console.WriteLine("A"); 
     for (int j = 0; j < 100; ++i) 
     { 
      Thread.Sleep(100); 
      yield return null; 
     } 
    } 
    } 

このソリューションを使用すると、最終的にはのtry/catch /の束またはもキャンセルについて知っておく必要があるメソッド呼び出しの束を持っている場合、実装するのは難しいかもしれません。

0

はい、正しく実行しています。最初は厄介なように思えるかもしれませんが、本当に最善の選択です。間違いなくスレッドを中止するよりはるかに優れています。ループの反復は、発見したように、CancelationPendingをチェックするのに理想的な候補です。これは、ループの反復処理では、論理作業単位が分離され、安全な点を簡単に記述できるためです。セーフポイントは、データの破損なしに終了を容易に達成できるスレッドの実行におけるマーカーです。

トリックは、キャンセルが正常に完了したことを発信者にタイムリーにフィードバックするのに十分頻繁に安全なポイントでポーリングすることですが、あまり頻繁にパフォーマンスに悪影響を及ぼしたり、 "コードを捨てる"ことはありません。

具体的なケースでは、内部ループがCancelationPendingをポーリングするのに最適な場所です。私は外側のループのチェックを省略します。理由は、内側のループがほとんどの時間が費やされているからです。外側のループは、内側のループが進むのを除いてほとんど実際の作業を行わないため、外側のループのチェックは無意味です。

ここで、GUI側で、キャンセルボタンがグレーアウトされ、キャンセル要求が受け入れられたことをユーザーに知らせることができます。 "cancelation pending"などのメッセージを表示して、それを明確にすることができます。キャンセルが完了したというフィードバックを受け取ったら、メッセージを削除することができます。

0

CPUを大量に消費するスレッドを中止しなければならない状況にある場合は、 'Abort'ブール値(または取り消しトークン)を1つのループまたは別のものでテストすることに固執しています(maybee最も内側のものではなく、どれくらいの時間がかかるかによって決まります)。AFAIK、内側のループからちょうど「戻る」ことができるので、メソッドを終了する - すべてのレベルでチェックする必要はありません!このオーバーヘッドを最小限に抑えるには、local-ishブール値にしてみてください。つまり、毎回半分の数のクラスを参照しないようにしてください。

Maybeeは 'Stoppable'のクラスを継承していますが、 'Abort'メソッドと 'Stop'ブール値がありますか?上の例のスレッドはほとんどの時間をスリープ状態にしているので、何かをチェックする前に平均待ち時間が50msになります。そのような場合には、寝る代わりにタイムアウトを使ってイベントを待つことができます。 'Abort'をオーバーライドしてイベントを設定し、継承したAbort &を呼び出すと、早めに待ちを終了します。 Danによって説明されているように、この新しい機能を実装する必要がある場合は、cancellationTokenデリゲート/コールバックでイベントを設定することもできます。

Windows APIなどは非常に少なくても簡単には「アンスティック」ではないし、非同期の「Ex」バージョンもないので、間違っている可能性があります。例えば。ソケットを強制的に読み取り、ソケットを強制的に読み取り、一時的なファイルを書き込んで、フォルダ変更通知を強制的に返します。

RGDS、 マーティン

関連する問題