Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "sleep" until timeout or cancellation is requested in .NET 4.0

What's the best way to sleep a certain amount of time, but be able to be interrupted by a IsCancellationRequested from a CancellationToken?

I'm looking for a solution which works in .NET 4.0.

I'd like to write

void MyFunc (CancellationToken ct)
{
   //... 
   // simulate some long lasting operation that should be cancelable 
   Thread.Sleep(TimeSpan.FromMilliseconds(10000), ct); 
}
like image 888
Onur Avatar asked Sep 10 '13 09:09

Onur


People also ask

Is there a wait command in C#?

Introduction to C# WaitC # wait is defined as waits for the task to finish its execution. It sources the current thread to wait until another thread calls its notify() or notifyAll() methods.

How do I make my program wait in C#?

Delay() Function This function is present in the “Systems Threading Tasks” namespace, so that this function will be used with the “Task” keyword. The Delay() function has the parameter, an integer variable that specifies the time for a task to be stopped and processes values in milliseconds.

How do I cancel my cancellation token?

When given a CancellationToken , you can create a new instance of the token source, assign it's token to the provided token, and cancel it. All other parties that can read this token will see that it's cancellation has been requested.

What is difference between CancellationTokenSource and CancellationToken?

A CancellationTokenSource object, which provides a cancellation token through its Token property and sends a cancellation message by calling its Cancel or CancelAfter method. A CancellationToken object, which indicates whether cancellation is requested.


3 Answers

I just blogged about it here:

CancellationToken and Thread.Sleep

in Short:

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

In your context:

void MyFunc (CancellationToken ct)
{
   //... 
   // simulate some long lasting operation that should be cancelable 
   var cancelled = ct.WaitHandle.WaitOne(TimeSpan.FromSeconds(10));
}
like image 63
Frode Avatar answered Oct 10 '22 01:10

Frode


Alternatively, I think this is pretty clear:

Task.Delay(waitTimeInMs, cancellationToken).Wait(cancellationToken);

like image 26
Fowl Avatar answered Oct 10 '22 02:10

Fowl


To cancel an asynchronious operation after a certain amount of time whilst still being able to cancel the operation manually use something like the following

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

This will cause a cancellation after five seconds. To cancel the operation your self all you have to do is pass the token into your async method and use the token.ThrowifCancellationRequested() method, where you have set up an event handler somewhere to fire cts.Cancel().

So a full example is:

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);

Where stopButton is the button you click to cancel the running task

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

and the method is defined as

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

Now, to enable you to work the thread but also enable user cancellation, you will need to write a 'pumping' method

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();
    }
}

Note, SomeAsyncMethod will return right to the caller. To block the caller aswell you will have to move the Task up in the call hierarchy.

like image 4
MoonKnight Avatar answered Oct 10 '22 03:10

MoonKnight