C# Task.Delay blocks second task - c#

I want to run two tasks.
StartAccessTokenTimer() runs every 60 seconds and refreshes accesstoken variable.
StartItemsTimer() will start after StartAccessTokenTimer() and work every 3 seconds if access token get.
private accessToken = "";
private async Task StartAccessTokenTimer()
{
CancellationTokenSource source = new CancellationTokenSource();
while (true)
{
accesstoken = await GetAccessToken();
await Task.Delay(TimeSpan.FromSeconds(3), source.Token);
}
}
private async Task StartItemsTimer()
{
CancellationTokenSource source = new CancellationTokenSource();
while (true)
{
var items = await GetItems(accessToken, "1");
await Task.Delay(TimeSpan.FromSeconds(60), source.Token);
}
}
public async Task StartOperations(){
await StartAccessTokenTimer();
await StartItemsTimer();
}
But it does not filre GetItems() methot. Because StartAccessTokenTimer() never start.. It fires GetAccessToken() continiously.

To trigger them to fire at the same time you can do the following:
public async Task StartOperations()
{
await Task.WhenAll(new Task[]
{
StartAccessTokenTimer(),
StartItemsTimer()
});
}

Related

Run periodic tasks using Rx.net: cancel and stop Observale.Interval stream after N seconds?

In the following code from the answer of What's a good way to run periodic tasks using Rx, with a single concurrent execution restriction?,
void Main()
{
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
using (timer.Do(x => Console.WriteLine("!")).Subscribe(tick => DoSomething()))
{
Console.ReadLine();
}
}
private void DoSomething()
{
Console.Write("<");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
Thread.Sleep(1000);
Console.WriteLine(">");
}
I'm trying to add cancellation and test stopping the program after five seconds.
using System.Reactive.Linq;
Task DoSomething(CancellationToken cancellationToken=default)
{
if (cancellationToken.IsCancellationRequested) { return Task.CompletedTask; }
Console.Write("<");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
Thread.Sleep(1000);
Console.WriteLine(">");
return Task.CompletedTask;
}
async Task WaitAndThenCancel(int seconds, CancellationTokenSource cancellationTokenSource)
{
await Task.Delay(seconds*1000);
Console.WriteLine("Cancelling...");
cancellationTokenSource.Cancel();
}
void Main(CancellationToken cancellationToken=default)
{
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
using (timer. Do(x =>
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Canceled - Main");
return; // Need to stop the stream here
}
Console.WriteLine("!");
}).Subscribe(async tick => await DoSomething(cancellationToken)))
{
Console.ReadLine();
}
}
var ct = new CancellationTokenSource();
WaitAndThenCancel(5, ct);
Main(ct.Token);
I expect the code to print the current time for N seconds, and then print "Canceled - Main" and stop. However, it starts to print "Canceled - Main" after N seconds and never stop?
!
<15:00:23.823>
!
<15:00:24.836>
!
<15:00:25.853>
!
<15:00:26.860>
!
<15:00:27.863Cancelling...
>
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
Canceled - Main
....
Using TakeUntil().
using System.Reactive;
using System.Reactive.Linq;
async Task DoSomething(CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
return; // Task.CompletedTask;
}
Console.Write("<");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
await Task.Delay(1000); // Thread.Sleep(1000);
Console.WriteLine(">");
}
async Task Main3(CancellationToken cancellationToken = default)
{
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
var cancel = Observable.Create<Unit>(observer => cancellationToken.Register(() => {
// observer.OnNext(default);
observer.OnCompleted(); }));
using (timer.Do(x =>
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Canceled - Main");
return;
}
Console.WriteLine("do!");
})
.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(5.0)))
.TakeUntil(cancel)
.Select(_ => Observable.FromAsync(() => DoSomething(cancellationToken)))
.Concat()
.Subscribe())
{
Console.WriteLine("Will wait for timed cancelation here.");
try
{
await Task.Delay(Timeout.Infinite, cancellationToken);
}
catch (OperationCanceledException)
{
Console.WriteLine($">{Environment.NewLine}Canceled - Main. In Using");
}
}
}
var ct = new CancellationTokenSource();
ct.CancelAfter(5000);
await Main3(ct.Token);
If you want to run an observable and stop it after a set time interval then you should simply use .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(5.0))).
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
var subscription =
timer
.Do(x => Console.WriteLine("!"))
.TakeUntil(Observable.Timer(TimeSpan.FromSeconds(5.0)))
.Subscribe(tick => DoSomething());
using (subscription)
{
Console.ReadLine();
}
If you want to use a CancellationToken then you could use this:
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
var cancel = Observable.Create<Unit>(observer => cts.Token.Register(() => { observer.OnNext(default); observer.OnCompleted(); }));
var subscription =
timer
.Do(x => Console.WriteLine("!"))
.TakeUntil(cancel)
.Subscribe(tick => DoSomething());
using (subscription)
{
await Task.Delay(TimeSpan.FromSeconds(5.0));
cts.Cancel();
Console.ReadLine();
}
Your Main is not canceling subscription cause Console.ReadLine(); prevents it from it. I suggest slightly different approach:
async Task Main(CancellationToken cancellationToken)
{
var timer = Observable.Interval(TimeSpan.FromMilliseconds(100));
using (timer.Do(x =>
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Canceled - Main");
return;
}
Console.WriteLine("!");
}).Subscribe(async tick => await DoSomething(cancellationToken)))
{
Console.WriteLine("Will wait for timed cancelation here.");
try
{
await Task.Delay(Timeout.Infinite, cancellationToken);
}
catch (OperationCanceledException)
{
Console.WriteLine($">{Environment.NewLine}Canceled - Main. In Using");
}
}
}
var ct = new CancellationTokenSource();
ct.CancelAfter(5000); // also no need to reinvent the wheel, just use the build in method for timed cancelation
await Main(ct.Token);
One more note Subscribe is not task-aware, i.e. it will not handle "truly" async methods correctly. For example if you change the DoSomething method to the following:
async Task DoSomething(CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
Console.Write("<");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff"));
await Task.Delay(1000);
Console.WriteLine(">");
}
The result will differ from what is expected/wanted. One approach to handle that is:
using (timer.Do(x =>
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Canceled - Main");
return;
}
Console.WriteLine("!");
})
.Select(_ => Observable.FromAsync(() => DoSomething(cancellationToken)))
.Concat()
.Subscribe())
{
// ...
}

cancelling tasks throws exception but task continues

I am trying to cancel a long-running task which is simulated bay an infinite loop printing to the console. Cancellation token is invoked from the main thread after 2 seconds. Even though the output on the console says "token IsCancellationRequested true", the loop continues. What is the problem here?
private static void CancelingTasks2()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
t = Task.Factory.StartNew(() =>
{
if (token.IsCancellationRequested)
throw new OperationCanceledException("cancelled on the token", token);
print("printing for ever ...");
}, token);
Thread.Sleep(2000);
cts.Cancel();
Console.WriteLine("canceled");
Console.WriteLine("task status " + t.Status);
Console.WriteLine("token IsCancellationRequested " + token.IsCancellationRequested);
}
private static void print(string txt)
{
while (true)
{
Console.WriteLine(txt); Thread.Sleep(500);
}
}
Use Task.Run instead of Task.Factory.StartNew and try to avoid mixing Task and Thread.Sleep. Use Task.Delay. If using Task then the code needs to be async all the way.
Your loop continues because there is nothing to break out of the loop.
A rewrite of the above example with proper syntax would look like this
public class Program {
public static async Task Main(string[] args) {
Console.WriteLine("Hello");
await CancelingTasks2();
Console.WriteLine("Exit");
}
private static async Task CancelingTasks2() {
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
var t = print("printing for ever ...", token);
await Task.Delay(2000);
cts.Cancel();
Console.WriteLine("canceled");
Console.WriteLine("task status " + t.Status);
Console.WriteLine("token IsCancellationRequested " + token.IsCancellationRequested);
}
private static async Task print(string txt, CancellationToken token) {
while (true) {
if (token.IsCancellationRequested)
throw new OperationCanceledException("cancelled on the token", token);
Console.WriteLine(txt);
await Task.Delay(500);
}
}
}
And produce the following output when run
Hello
printing for ever ...
printing for ever ...
printing for ever ...
printing for ever ...
printing for ever ...
canceled
task status WaitingForActivation
token IsCancellationRequested True
Exit
Fiddle
Looks like you do an infinite loop in print, not checking the token cancellation more than once.
You should put you if inside the while loop:
while (true)
{
if (token.IsCancellationRequested)
{
throw new OperationCanceledException(
"cancelled on the token", token);
}
Console.WriteLine(txt);
Thread.Sleep(500);
}

Stop Tasks with token used in an array of tasks

I have this example:
var asyncTasks = new Task[list.Length];
for (int i = 0; i< list.Length; i++)
{
asyncTasks[i] = tf(cts.Token, list[i]);
}
await Task.WhenAll(asyncTasks);
//do other stuffs when all tasks completed
and of course i have my async function called tf:
private async Task tf(CancellationToken token , string list)
{
try
{
await Task.Run(() =>
{
if (token.IsCancellationRequested)
{
MessageBox.Show("Stopped", "Operation Aborted");
token.ThrowIfCancellationRequested();
cts = new CancellationTokenSource();
}
// do something
}, token);
}catch{}
cts = new CancellationTokenSource();
}
EDIT: in my cancel button i declare: cts.Cancel();
This method works properly for me because it executes an array of tasks at the same time, but in my opinion I'm not able to send token for cancellation request because all cts.Token already assigned are valid, so if in another button i try to cts.Cancel() this won't work. How can i do the same but making cancellation token request valid?
The problem was that i reset the token for every call with cts = new CancellationTokenSource(); at the end of task function
Function fixed:
private async Task tf(CancellationToken token , string list)
{
try
{
await Task.Run(() =>
{
if (token.IsCancellationRequested)
{
MessageBox.Show("Stopped", "Operation Aborted");
token.ThrowIfCancellationRequested();
cts = new CancellationTokenSource();
}
// do something
}, token);
}catch{}
}

Running a task periodically with a cancellation token called from another method

I am trying to run a screenshot task every x amount of minutes, but it seems that when i want to cancel that task using the given cancellation token, it doesn't seem to do anything.
Here is my start method code:
var CancellationTokenSource = new CancellationTokenSource();
CancellationTokenSource?.Cancel();
CancellationTokenSource = new CancellationTokenSource();
var token = CancellationTokenSource.Token;
await RunPeriodically(async () =>
{
var screenCaptured = TakeScreenshot();
if (screenCaptured == null || CancellationTokenSource.Token.IsCancellationRequested)
return;
var correctUserName = Settings.Default.Username.Split('.');
var parsedUsername = correctUserName[0] + " " + correctUserName[1];
await ScreenshotHelper.UploadScreenshotAsync(ProjectName, "screenshotscontainer",
screenCaptured.ToArray(), Environment.MachineName, parsedUsername);
Console.WriteLine("Took Screenshot: " + DateTime.Now.ToString(CultureInfo.InvariantCulture));
}, TimeSpan.FromSeconds(3), token);
and here is the run periodically code:
public async Task RunPeriodically(Action action, TimeSpan interval, CancellationToken token)
{
while (true)
{
action();
await Task.Delay(interval, token);
}
}
I thought I'd knock up a quick example of a class that will run a process, wait a period of time and start the process again without blocking any threads.
class RepeatableProcess
{
private Timer processTimer;
private int delay;
private CancellationTokenSource source;
private CancellationToken token;
private Action processToRun;
private bool canStart = true;
public RepeatableProcess(int delaySeconds,Action process)
{
delay = delaySeconds;
processToRun = process;
}
public void Start()
{
if (canStart)
{
canStart = false;
source = new CancellationTokenSource();
token = source.Token;
processTimer = new Timer(TimedProcess, token, Timeout.Infinite, Timeout.Infinite);
processTimer.Change(0, Timeout.Infinite);
}
}
public void Stop()
{
source.Cancel();
}
public void TimedProcess(object state)
{
CancellationToken ct = (CancellationToken)state;
if (ct.IsCancellationRequested)
{
Console.WriteLine("Timer Stopped");
processTimer.Dispose();
canStart = true;
}
else
{
processToRun.Invoke();
processTimer.Change(delay, Timeout.Infinite);
}
}
}
The Start method creates a timer that never starts and never repeats.
It then starts the process immediately, to run only once.
The TimedProcess method checks for cancellation and runs the specified process . After the process completes the timer is set to start after the specified delay and run only once.
When the timer fires it gets a thread from the thread pool. There can be no overrun issues because the timer is not set to run again until the process is finished.
This class would need more protection but it is just an example.
Hope this helps.

How to cancel await Task.Delay()?

As you can see in this code:
public async void TaskDelayTest()
{
while (LoopCheck)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000);
}
}
}
I want it to set textbox to string value of i with one second period until I set LoopCheck value to false . But what it does is that it creates all iteration ones in for all and even if I set LoopCheck value to false it still does what it does asyncronously.
I want to cancel all awaited Task.Delay() iteration when I set LoopCheck=false. How can I cancel it?
Use the overload of Task.Delay which accepts a CancellationToken
public async Task TaskDelayTest(CancellationToken token)
{
while (LoopCheck)
{
token.throwIfCancellationRequested();
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(1000, token);
}
}
}
var tokenSource = new CancellationTokenSource();
TaskDelayTest(tokenSource.Token);
...
tokenSource.Cancel();
If you're going to poll, poll on a CancellationToken:
public async Task TaskDelayTestAsync(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
textBox1.Text = i.ToString();
await Task.Delay(TimeSpan.FromSeconds(1), token);
}
}
For more information, see the cancellation documentation.
Just a slight comment about having a cancellation token, and using a try-catch to stop it throwing an exception - your iteration block might fail due to a different reason, or it might fail due to a different task getting cancelled (e.g. from an http request timing out in a sub method), so to have the cancellation token not throw an exception you might want a bit more complicated catch block
public async void TaskDelayTest(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
for (int i = 0; i < 100; i++)
{
try
{
textBox1.Text = i.ToString();
await DoSomethingThatMightFail();
await Task.Delay(1000, token);
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
//task is cancelled, return or do something else
return;
}
catch(Exception ex)
{
//this is an actual error, log/throw/dostuff here
}
}
}
}
After running into this problem I wrote a drop in replacement that behaves as expected if you want to do polling:
public static class TaskDelaySafe
{
public static async Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
{
await Task.Delay(TimeSpan.FromMilliseconds(millisecondsDelay), cancellationToken);
}
public static async Task Delay(TimeSpan delay, CancellationToken cancellationToken)
{
var tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var task = new TaskCompletionSource<int>();
tokenSource.Token.Register(() => task.SetResult(0));
await Task.WhenAny(
Task.Delay(delay, CancellationToken.None),
task.Task);
}
}
It uses a cancellation token callback to complete a task and then awaits either that synthetic task or the normal Task.Delay with no cancellation token. This way it won't throw an exception when the source token is cancelled, but still responds to the cancellation by returning execution. You still need to check the IsCancellationRequested after calling it to decide what to do if it is cancelled.
Unit tests, if anyone is interested:
[Test]
public async Task TaskDelay_WaitAlongTime()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
await Base.Framework.TaskDelaySafe.Delay(System.TimeSpan.FromSeconds(5), System.Threading.CancellationToken.None);
Assert.IsTrue(sw.Elapsed > System.TimeSpan.FromSeconds(4));
}
[Test]
public async Task TaskDelay_DoesNotWaitAlongTime()
{
var tokenSource = new System.Threading.CancellationTokenSource(250);
var sw = System.Diagnostics.Stopwatch.StartNew();
await Base.Framework.TaskDelaySafe.Delay(System.TimeSpan.FromSeconds(5), tokenSource.Token);
Assert.IsTrue(sw.Elapsed < System.TimeSpan.FromSeconds(1));
}
[Test]
public async Task TaskDelay_PrecancelledToken()
{
var tokenSource = new System.Threading.CancellationTokenSource();
tokenSource.Cancel();
var sw = System.Diagnostics.Stopwatch.StartNew();
await Base.Framework.TaskDelaySafe.Delay(System.TimeSpan.FromSeconds(5), tokenSource.Token);
Assert.IsTrue(sw.Elapsed < System.TimeSpan.FromSeconds(1));
}
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static DateTime start;
static CancellationTokenSource tokenSource;
static void Main(string[] args)
{
start = DateTime.Now;
Console.WriteLine(start);
TaskDelayTest();
TaskCancel();
Console.ReadKey();
}
public static async void TaskCancel()
{
await Task.Delay(3000);
tokenSource?.Cancel();
DateTime end = DateTime.Now;
Console.WriteLine(end);
Console.WriteLine((end - start).TotalMilliseconds);
}
public static async void TaskDelayTest()
{
tokenSource = new CancellationTokenSource();
try
{
await Task.Delay(2000, tokenSource.Token);
DateTime end = DateTime.Now;
Console.WriteLine(end);
Console.WriteLine((end - start).TotalMilliseconds);
}
catch (TaskCanceledException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
tokenSource.Dispose();
tokenSource = null;
}
}
}
}

Categories

Resources