Running a task periodically without blocking the main thread C# - c#

I am trying to send a keep-alive HTTP request each 30 minutes. However I have other methods to be called at the time in between, so what I tried to do is:
Task.Factory.StartNew(async () =>
{
while(true){
await Task.Delay(new TimeSpan(0,30,0), CancellationToken.None);
await FooTask();
}
});
Am I using it properly?

Are you doing it properly? No. You say you want a loop, but don't write a loop. You're also using the wrong task creation function:
Task.Run(async () =>
{
while(true)
{
await FooTask().ConfigureAwait(false);
await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false);
}
});
There's also PeriodicTimer which follows a similar pattern, you do your action and await the next tick.

I'd suggest using Microsoft's Reactive Framework.
Then you can just do this:
IDisposable subscription =
Observable
.Timer(TimeSpan.Zero, TimeSpan.FromMinutes(30.0))
.SelectMany(_ => Observable.FromAsync(() => FooTask()))
.Subscribe();
Calling subscription.Dispose() shuts it down. It's nice and simple.

You likely want to use Task.Run() instead of Task.Factory.StartNewAsync(), per the remarks in the docs. Task.Run() is recommended unless you need the extra options in Task.Factory.StartNewAsync() (ex: Task Creation Options, Task Scheduler, Parameter Passing that Task.Run() does not cover).
Task.Run() will queue the task to run on the current thread pool, and it will also return a handle to the task in case you want to Wait for completion or Cancel the task, as shown in the below example.
CancellationTokenSource src = new CancellationTokenSource();
CancellationToken ct = src.Token;
Task t = Task.Run(async () =>
{
while(true){
ct.ThrowIfCancellationRequested();
await Task.Delay(new TimeSpan(0,30,0), ct);
await FooTask();
}
}, ct);
// Do some other stuff while your task runs
// Cancel Your Task, OR
ct.Cancel();
// Wait for your task to finish
try
{
t.Wait();
}
catch (AggregateException e)
{
// Handle the exception
}

Related

Why doesn't it work to use ContinueWith to run task sequentially?

My goal was to start "Task2" after "Task1". At first I wrote code like "Code1" below, but it did not work (Task2 started before Task1 was finished). So I searched Stackoverflow and modified my code like "Code2" below as the existing answer suggested. I wonder why "Code1" did not work.
Code1
static void Main(string[] args)
{
var p = new Program();
p.Test2();
Console.ReadKey();
}
void Test2()
{
Task.Factory.StartNew(async () =>
{
await Task1();
}).ContinueWith((t) => {
Task2();
});
}
async Task Task1()
{
Debug.WriteLine("Task 1 starting....");
await LongTask();
Debug.WriteLine("Task 1 done");
}
Task LongTask()
{
return Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
});
}
void Task2()
{
Debug.WriteLine("Task 2");
}
Code2
Task.Factory.StartNew(async () =>
{
await Task1();
Task2();
}).ContinueWith((t) => {
//Task2();
});
Because when you are running task like Task.Factory.StartNew(async () => ... it returns Task<Task> (task of task). And first task terminates without waiting for the inner Task.
For preventing this situation, you can use Unwrap method:
Task.Factory.StartNew(async () =>
{
await Task1();
})
.Unwrap()
.ContinueWith((t) => {
Task2();
});
That StartNew will return Task<Task>, and it seems you want to execute continuation after inner task completion. That's why we need Unwrap.
It “unwraps” the inner task that’s returned as the result of the outer task. Calling Unwrap on a Task gives you back a new Task (which we often refer to as a proxy) which represents the eventual completion of the inner task. And then, we are adding continuation to the inner task.
But, as Task.Run will do that unwrapping automatically you can use Task.Run in that case:
Task.Run(async () =>
{
await Task1();
})
.ContinueWith((t) => {
Task2();
});
And that can be simplified to:
Task.Run(async () =>
{
await Task1();
Task2();
});
Detailed information about diferences between Task.Run and Task.Factory.StartNew:
Read more here from Stephen Toub who is an engineer in the .Net team. The reason of decision in case of Task.Run:
Because we expect it to be so common for folks to want to offload work
to the ThreadPool, and for that work to use async/await, we decided to
build this unwrapping functionality into Task.Run.
By the way, as Stephen recommended just try to use Task.Run in most cases, but this in no way obsoletes Task.Factory.StartNew, there is still some places in which Task.Factory.StartNew must be used:
Task.Factory.StartNew still has many important (albeit more advanced)
uses. You get to control TaskCreationOptions for how the task
behaves. You get to control the scheduler for where the task should
be queued to and run. You get to use overloads that accept object
state, which for performance-sensitive code paths can be used to avoid
closures and the corresponding allocations. For the simple cases,
though, Task.Run is your friend.
Task.Factory.StartNew don't understand async lambdas. For Task.Factory.StartNew your lambda is just function returning Task. It doesn't automatically await that task. Also, note that using Task.Factory.StarNew and ContinueWith is discouraged in modern C# as it hard to use correctly (please read, "StartNew is Dangerous" by Stephen Cleary). You should use Task.Factory.StarNew or ContinueWith only if you can't do without them.
Instead, you can just use async/await one more time:
async Task Test2()
{
await Task1();
Task2();
}
or Task.Run:
async Task Test2()
{
return Task.Run(await () =>
{
await Task1();
Task2();
});
}
The second approach might be convenient if you want to make sure that your asynchronous Task1() is started on background thread.

C# - Parallel.ForEach and Async combination

I've a problem with parallel.foreach and async combination.
Here is my code -
new Thread(() =>{
//doing some stuff here
Parallel.ForEach(.....,ParallelOption,async(j,loopState) =>
{
//await some stuff here like:
// HttpResponseMessage res = await httpClient.GetAsync(url);
// UpdateUI
}
}).Start();
Now, my problem is how can I be sure of the loop has completed all jobs? It just ends in few seconds but UIUpdateing will continue for much more time.
How its possible to wait for the await httpClient.GetAsync(url) to complete and then update the UI?
Using async/await for multiple tasks
return Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
I don't know what's in your actual loop code. If you put more up I can make this more specific. Basically though you use WhenAll to await all of the ascnc tasksf

Task being marked as RanToCompletion at await, when still Running

I'm still getting up to speed with async & multi threading. I'm trying to monitor when the Task I Start is still running (to show in a UI). However it's indicating that it is RanToCompletion earlier than I want, when it hits an await, even when I consider its Status as still Running.
Here is the sample I'm doing. It all seems to be centred around the await's. When it hits an await, it is then marked as RanToCompletion.
I want to keep track of the main Task which starts it all, in a way which indicates to me that it is still running all the way to the end and only RanToCompletion when it is all done, including the repo call and the WhenAll.
How can I change this to get the feedback I want about the tskProdSeeding task status?
My Console application Main method calls this:
Task tskProdSeeding;
tskProdSeeding = Task.Factory.StartNew(SeedingProd, _cts.Token);
Which the runs this:
private async void SeedingProd(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
await Task.WhenAll(Task.Run(async () =>
{
foreach (var code in codesToSeed)
{
if (!token.IsCancellationRequested)
{
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
}
}, token));
Thread.Sleep(30000);
}
}
If you use async void the outer task can't tell when the task is finished, you need to use async Task instead.
Second, once you do switch to async Task, Task.Factory.StartNew can't handle functions that return a Task, you need to switch to Task.Run(
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
Once you do both of those changes you will be able to await or do a .Wait() on tskProdSeeding and it will properly wait till all the work is done before continuing.
Please read "Async/Await - Best Practices in Asynchronous Programming" to learn more about not doing async void.
Please read "StartNew is Dangerous" to learn more about why you should not be using StartNew the way you are using it.
P.S. In SeedingProd you should switch it to use await Task.Delay(30000); insetad of Thread.Sleep(30000);, you will then not tie up a thread while it waits. If you do this you likely could drop the
tskProdSeeding = Task.Run(() => SeedingProd(_cts.Token), _cts.Token);
and just make it
tskProdSeeding = SeedingProd(_cts.Token);
because the function no-longer has a blocking call inside of it.
I'm not convinced that you need a second thread (Task.Run or StartNew) at all. It looks like the bulk of the work is I/O-bound and if you're doing it asynchronously and using Task.Delay instead of Thread.Sleep, then there is no thread consumed by those operations and your UI shouldn't freeze. The first thing anyone new to async needs to understand is that it's not the same thing as multithreading. The latter is all about consuming more threads, the former is all about consuming fewer. Focus on eliminating the blocking and you shouldn't need a second thread.
As others have noted, SeedingProd needs to return a Task, not void, so you can observe its completion. I believe your method can be reduced to this:
private async Task SeedingProd(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
int totalSeeded = 0;
var codesToSeed = await _myRepository.All().ToListAsync(token);
foreach (var code in codesToSeed)
{
if (token.IsCancellationRequested)
return;
try
{
int seedCountByCode = await _myManager.SeedDataFromLive(code);
totalSeeded += seedCountByCode;
}
catch (Exception ex)
{
_logger.InfoFormat(ex.ToString());
}
}
await Task.Dealy(30000);
}
}
Then simply call the method, without awaiting it, and you'll have your task.
Task mainTask = SeedingProd(token);
When you specify async on a method, it compiles into a state machine with a Task, so SeedingProd does not run synchronously, but acts as a Task even if returns void. So when you call Task.Factory.StartNew(SeedingProd) you start a task that kick off another task - that's why the first one finishes immediately before the second one. All you have to do is add the Task return parameter instead of void:
private async Task SeedingProdAsync(CancellationToken ct)
{
...
}
and call it as simply as this:
Task tskProdSeeding = SeedingProdAsync(_cts.Token);

3 parallel tasks each awaiting their own result

I would like to try using background workers. I am interesting to use async/await.
I have 3 parallel tasks.
private async void RunDownloadsAsync()
{
Task taskDelay1 = Task.Run(() => Task.Delay(10000));
Task taskDelay2 = Task.Run(() => Task.Delay(15000));
Task taskDelay3 = Task.Run(() => Task.Delay(20000));
await ???
}
Let's assume each of these Tasks changes the value of 3 Labels (label1, label2, label3) on a form when they are done. i.e. all labels are set to "Pending" and when each task is done, their corresponding label value will change to "Finished".
I would like to await each task accordingly so that they each do their own corresponding task. That means if taskDelay1's job is finished set label1's text to "Finished" while taskDelay2 and taskDelay3 are still pending. But if I put 3 awaits there, the program will wait for all of them to finish their task and then continue the rest of the code.
How can I continue the rest of the code in which each label only waits for its own task to finish and then decide what to do?
You should use Task.WhenAll method to wait for all of them and ContinueWith to execute operation when the task is completed.
Task taskDelay1 = Task.Run(() => Task.Delay(10000)).ContinueWith(t => SetLabel());
Task taskDelay2 = Task.Run(() => Task.Delay(15000)).ContinueWith(t => SetLabel());
Task taskDelay3 = Task.Run(() => Task.Delay(20000)).ContinueWith(t => SetLabel());
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
There's an important distinction between parallel and concurrent. Parallel refers to using multiple threads to do CPU-bound work. Parallelism is one form of concurrency, but not the only one. Another form of concurrency is asynchrony, which can do non-CPU-bound work without introducing multiple threads.
In your case, "downloads" imply an I/O-bound operation, which should be done with asynchrony and not parallelism. Check out the HttpClient class for asynchronous downloads. You can use Task.WhenAll for asynchronous concurrency:
private async Task RunDownload1Async()
{
label1.Text = "Pending";
await Task.Delay(10000);
label1.Text = "Finished";
}
private async Task RunDownload2Async()
{
label2.Text = "Pending";
await Task.Delay(15000);
label2.Text = "Finished";
}
private async Task RunDownload3Async()
{
label3.Text = "Pending";
await Task.Delay(20000);
label3.Text = "Finished";
}
private async Task RunDownloadsAsync()
{
await Task.WhenAll(RunDownload1Async(), RunDownload2Async(), RunDownload3Async());
}
This approach avoids creating unnecessary threads and also does not use outdated techniques (ContinueWith, Dispatcher.Invoke). That said, it's not perfect, since the GUI logic is mixed with the operational logic in the RunDownloadNAsync methods. A better approach would be to use IProgress<T> and Progress<T> for updating the UI.
You can use ContinueWith to execute an action after the Task is completed. Note that you might need to call Dispatcher.Invoke in the expression in the ContinueWith to prevent cross-thread calls.
Here is a sample from a WPF application, using Dispatcher.Invoke:
private static async void Async()
{
Task taskDelay1 = Task.Run(() => Task.Delay(1000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label1.Content = "One done"));
Task taskDelay2 = Task.Run(() => Task.Delay(1500))
.ContinueWith(x => Dispatcher.Invoke(() => this.label2.Content = "Two done"));
Task taskDelay3 = Task.Run(() => Task.Delay(2000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label3.Content = "Three done"));
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
}
As Dirk suggested, an alternative to calling Dispatcher.Invoke would be to use a task scheduler, e.g. TaskScheduler.FromCurrentSynchronizationContext() if you're on the UI thread.

Regarding the usage of SemaphoreSlim with Async/Await

I am not an advanced developer. I'm just trying to get a hold on the task library and just googling. I've never used the class SemaphoreSlim so I would like to know what it does. Here I present code where SemaphoreSlim is used with async & await but which I do not understand. Could someone help me to understand the code below.
1st set of code
await WorkerMainAsync();
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
while (true)
{
await ss.WaitAsync();
// you should probably store this task somewhere and then await it
var task = DoPollingThenWorkAsync();
}
}
async Task DoPollingThenWorkAsync(SemaphoreSlim semaphore)
{
var msg = Poll();
if (msg != null)
{
await Task.Delay(3000); // process the I/O-bound job
}
// this assumes you don't have to worry about exceptions
// otherwise consider try-finally
semaphore.Release();
}
Firstly, the WorkerMainAsync will be called and a SemaphoreSlim is used. Why is 10 passed to the constructor of SemaphoreSlim?
When does the control come out of the while loop again?
What does ss.WaitAsync(); do?
The DoPollingThenWorkAsync() function is expecting a SemaphoreSlim but is not passed anything when it is called. Is this typo?
Why is await Task.Delay(3000); used?
They could simply use Task.Delay(3000) but why do they use await here instead?
2nd set of code for same purpose
async Task WorkerMainAsync()
{
SemaphoreSlim ss = new SemaphoreSlim(10);
List<Task> trackedTasks = new List<Task>();
while (DoMore())
{
await ss.WaitAsync();
trackedTasks.Add(Task.Run(() =>
{
DoPollingThenWorkAsync();
ss.Release();
}));
}
await Task.WhenAll(trackedTasks);
}
void DoPollingThenWorkAsync()
{
var msg = Poll();
if (msg != null)
{
Thread.Sleep(2000); // process the long running CPU-bound job
}
}
Here is a task & ss.Release added to a list. I really do not understand how tasks can run after adding to a list?
trackedTasks.Add(Task.Run(async () =>
{
await DoPollingThenWorkAsync();
ss.Release();
}));
I am looking forward for a good explanation & help to understand the two sets of code. Thanks
why 10 is passing to SemaphoreSlim constructor.
They are using SemaphoreSlim to limit to 10 tasks at a time. The semaphore is "taken" before each task is started, and each task "releases" it when it finishes. For more about semaphores, see MSDN.
they can use simply Task.Delay(3000) but why they use await here.
Task.Delay creates a task that completes after the specified time interval and returns it. Like most Task-returning methods, Task.Delay returns immediately; it is the returned Task that has the delay. So if the code did not await it, there would be no delay.
just really do not understand after adding task to list how they can run?
In the Task-based Asynchronous Pattern, Task objects are returned "hot". This means they're already running by the time they're returned. The await Task.WhenAll at the end is waiting for them all to complete.

Categories