2013-12-17 10 views
6

ロードと呼ばれるUIボタンがあります。スレッドはスレッドを生成し、スレッドを生成します。タスクの待機があり、終了するとタスクはキャンセルされます。ロードボタンは無効になっておらず、ユーザーは複数回クリックできます。クリックするたびに前のタスクをキャンセルする必要があります。CancellationTokenとCancellationTokenSource-どのように使用するのですか?

私はCancellationTokenSourceとCancellationTokenをどのように使うべきか混乱しています。 Beowはコードです。あなたはそれをどのように使用するか、以下の使用法に問題があるかどうかを教えてください。非同期ではありません。私たちはまだそこにいません。

CancellationTokenSource _source = new CancellationTokenSource(); 
     public void OnLoad() 
     { 
      //Does this cancel the previously spawned task? 
      _source.Cancel(); 
      _source.Dispose(); 
      _source = new CancellationTokenSource(); 
      var activeToken = _source.Token; 
      //Do I need to do the above all the time or is there an efficient way? 

      Task.Factory.StartNew(() => 
       { 
        var child = Task.Factory.StartNew(() => 
         { 
          Thread.Sleep(TimeSpan.FromSeconds(20)); 
          activeToken.ThrowIfCancellationRequested(); 
         }, activeToken); 

        if (!child.Wait(TimeSpan.FromSeconds(5))) 
        { 
         _source.Cancel(); 
        } 
       }); 
     } 

は、私が以前に生成されたタスクをキャンセルする必要があり、すべての生成されたタスクはタイムアウトを持っている必要があります。

+0

を私は後にトークンをキャンセルする方法で構築されたあると思う程度理由により簡単に何が起こっているか確認する必要があり最初の3つのことを修正

一定のタイムアウト。 – CodesInChaos

+0

http://stackoverflow.com/a/16607800/34397 – SLaks

+0

@ SLaks - それは私が.NET 4.0上で動作しません – Mike

答えて

5

最初に、Visual Studio 2012+を使用している場合は、Microsoft.Bcl.Asyncパッケージを追加してasyncおよびその他の高度な機能のサポートを.NET 4.0プロジェクトに追加できます。

Visual Studio 2010を使用している場合は、ParallelExtensionsExtrasライブラリに付属のWithTimeout拡張メソッドを使用できます。このメソッドは、元のTaskをTaskCompletionSourceでラップし、タイマーが満了するとSetCancelledを呼び出すタイマーをラップします。あなたがで必要な動作を作成することができ、一般的には

var myTask=Task.Factory.StartNew(()=>{...}) 
      .WithTimeout(TimeSpan.FromSeconds(20)); 

コードがhereですが、実際の方法は簡単です:右あなたのタスクを作成した後

/// <summary>Creates a new Task that mirrors the supplied task but that 
    /// will be canceled after the specified timeout.</summary> 
    /// <typeparam name="TResult">Specifies the type of data contained in the 
    /// task.</typeparam> 
    /// <param name="task">The task.</param> 
    /// <param name="timeout">The timeout.</param> 
    /// <returns>The new Task that may time out.</returns> 
    public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, 
                  TimeSpan timeout) 
    { 
     var result = new TaskCompletionSource<TResult>(task.AsyncState); 
     var timer = new Timer(state => 
         ((TaskCompletionSource<TResult>)state).TrySetCanceled(), 
         result, timeout, TimeSpan.FromMilliseconds(-1)); 
     task.ContinueWith(t => 
     { 
      timer.Dispose(); 
      result.TrySetFromTask(t); 
     }, TaskContinuationOptions.ExecuteSynchronously); 
     return result.Task; 
    } 

は、あなたがそれを使用することができます設定したイベントまたは条件に応じてSetResult、SetCancelledメソッドを呼び出すTaskCompletionSourceを作成します。

0

あなたのコードには混乱を招くいくつかの間違いがあります。

まず、Task.Delayの代わりにThread.Sleepやその他のタイマーベースのメソッドを使用しています(Task.Delayにアクセスできない場合は、自分で作成することを強くお勧めします)。スリープはブロッキング待ちであり、キャンセルトークンで条件付けすることはできません。その結果、操作がキャンセルされた場合でも、貴重なスレッドプールスレッドが複数秒間人質に拘束されます。これにより、後のボタンの押下が以前のボタン押下の影響を受けてしまう可能性がある。

第二には、待ち時間の終わりに、あなたが_sourceをキャンセルしているが、これはボタンが押された時にソースない値の現在 _valueを指します。以前のボタンを押すと、後で自分のボタンを押すのではなく、そのボタンを押したままにします。

第3に、取り消しトークンソースを1つのスレッドに配置し、別のスレッドで取り消しトークンソースを取り消します。あなたはオブジェクトが例外を処理していないことは幸運です。

第4に、この種の状況ではasyncを使用することが理想的です。あなたはあなたが.Net 4.0上にいるだけだと言った。

CancellationTokenSource _prevSource = new CancellationTokenSource(); 
public void OnButtonPress() { 
    var curSource = new CancellationTokenSource(); 
    _prevSource.Cancel(); 
    _prevSource = curSource; 

    MyCustomDelay(TimeSpan.FromSeconds(5), curSource.Token).ContinueWith(t => { 
     curSource.Cancel(); 
    }, TaskContinuationOptions.OnlyOnRanToCompletion); 

    var r = MyCustomDelay(TimeSpan.FromSeconds(20), curSource.Token).ContinueWith(t => { 
     curSource.ThrowIfCancellationRequested(); 
    }, TaskContinuationOptions.OnlyOnRanToCompletion); 
    // after 5 seconds the token r's delay is conditions on is cancelled 
    // so r is cancelled, due to the continuation specifying OnlyOnRanToCompletion 
    // the ThrowIfCancellationRequested line won't be executed 
    // although if we removed the cancel-after-5-seconds bit then it would be 
} 
+1

.NET 4、Task.Delayはありません –

+0

@Panagiotis私はそれをタイマーで実装するように修正しました。ブロッキングのコストは、実装する価値があります。 –

2

これはそれを行うだろう:

private CancellationTokenSource _cancelTasks; 

    // this starts your process 
    private void DoStuff() 
    { 
     _cancelTasks = new CancellationTokenSource(); 

     var task = new Task(() => { /* your actions here */ }, _cancelTasks.Token); 
     task.Start(); 

     if (!task.Wait(5000)) _cancelTasks.Cancel(); 
    } 
関連する問題