72

一定の時間スリープするのに最適な方法は何IsCancellationRequestedですかCancellationToken?

.NET 4.0 で動作するソリューションを探しています。

書きたい

void MyFunc (CancellationToken ct)
{
   //... 
   // simulate some long lasting operation that should be cancelable 
   Thread.Sleep(TimeSpan.FromMilliseconds(10000), ct); 
}
4

5 に答える 5

131

ここでそれについてブログを書きました:

CancellationToken と Thread.Sleep

要するに:

var cancelled = token.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));

あなたの文脈では:

void MyFunc (CancellationToken ct)
{
   //... 
   // simulate some long lasting operation that should be cancelable 
   var cancelled = ct.WaitHandle.WaitOne(TimeSpan.FromSeconds(10));
}
于 2014-05-13T13:50:49.670 に答える
4

操作を手動でキャンセルできる一方で、一定時間後に非同期操作をキャンセルするには、次のようなものを使用します

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
cts.CancelAfter(5000);

これにより、5 秒後にキャンセルされます。自分で操作をキャンセルするには、tokenを async メソッドに渡し、メソッドを使用するtoken.ThrowifCancellationRequested()だけです。ここで、イベント ハンドラーをどこかに発火するように設定しますcts.Cancel()

したがって、完全な例は次のとおりです。

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
cts.CancelAfter(5000);

// Set up the event handler on some button.
if (cancelSource != null)
{
    cancelHandler = delegate
    {
        Cancel(cts);
    };
    stopButton.Click -= cancelHandler;
    stopButton.Click += cancelHandler;
}

// Now launch the method.
SomeMethodAsync(token);

stopButton実行中のタスクをキャンセルするためにクリックするボタンはどこにありますか

private void Cancel(CancellationTokenSource cts)
{
    cts.Cancel();
}

メソッドは次のように定義されます。

SomeMethodAsync(CancellationToken token)
{
    Task t = Task.Factory.StartNew(() => 
        {
            msTimeout = 5000;
            Pump(token);
        }, token,
           TaskCreationOptions.None,
           TaskScheduler.Default);
}

ここで、スレッドを操作できるようにするだけでなく、ユーザーによるキャンセルも可能にするには、「ポンピング」メソッドを記述する必要があります。

int msTimeout;
bool timeLimitReached = false;
private void Pump(CancellationToken token)
{
    DateTime now = DateTime.Now;
    System.Timer t = new System.Timer(100);
    t.Elapsed -= t_Elapsed;
    t.Elapsed += t_Elapsed;
    t.Start();
    while(!timeLimitReached)
    {
        Thread.Sleep(250);
        token.ThrowIfCancellationRequested();
    }
}

void t_Elapsed(object sender, ElapsedEventArgs e)
{
    TimeSpan elapsed = DateTime.Now - this.readyUpInitialised;
    if (elapsed > msTimeout)
    {
        timeLimitReached = true;
        t.Stop();
        t.Dispose();
    }
}

注、SomeAsyncMethod呼び出し元にすぐに戻ります。Task発信者もブロックするには、呼び出し階層を上に移動する必要があります。

于 2013-09-10T09:28:57.353 に答える
2

CancellationToken.WaitHandleは、CancellationTokenSource が破棄された後にアクセスされると、例外をスローできます。

ObjectDisposedException: CancellationTokenSource が破棄されました。

場合によっては、特にリンクされたキャンセル ソースが手動で破棄されている場合 (そうあるべきです)、これは迷惑になる可能性があります。

この拡張メソッドにより、「安全なキャンセル待ち」が可能になります。ただし、キャンセル トークンの状態および/または戻り値の使用のチェックおよび適切なフラグ付けと組み合わせて使用​​する必要があります。これは、WaitHandle のアクセスに対する例外を抑制し、予想よりも速く返される可能性があるためです。

internal static class CancellationTokenExtensions
{
    /// <summary>
    /// Wait up to a given duration for a token to be cancelled.
    /// Returns true if the token was cancelled within the duration
    /// or the underlying cancellation token source has been disposed.
    /// </summary>
    public static bool WaitForCancellation(this CancellationToken token, TimeSpan duration)
    {
        WaitHandle handle;
        try
        {
            handle = token.WaitHandle;
        }
        catch
        {
            /// The source of the token was disposed (already cancelled)
            return true;
        }

        if (handle.WaitOne(duration))
        {
            /// A cancellation occured during the wait
            return true;
        }
        else
        {
            /// No cancellation occured during the wait
            return false;
        }
    }
}
于 2019-06-21T17:05:55.437 に答える
1

これまでに見つけた最良の解決策は次のとおりです。

void MyFunc(CancellationToken ct)
{
  //...
  var timedOut = WaitHandle.WaitAny(new[] { ct.WaitHandle }, TimeSpan.FromMilliseconds(2000)) == WaitHandle.WaitTimeout;
  var cancelled = ! timedOut;
}

アップデート:

これまでの最善の解決策は、受け入れられた答えです。

于 2013-09-10T09:24:48.390 に答える