I need a way to set an async task as long running without using Task.Factory.StartNew(...) and instead using Task.Run(...) or something similar.
Context:
I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread). This can be achieved through the code below:
var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
The problem is that Task.Factory.StartNew(...) does not return the active async task that is passed in but rather a 'task of running the Action' which functionally always has taskStatus of 'RanToCompletion'. Since my code needs to be able to track the task's status to see when it becomes 'Canceled' (or 'Faulted') I need to use something like below:
var cts = new CancellationTokenSource();
Task t = Task.Run(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token);
Task.Run(...), as desired, returns the async process itself allowing me to obtain actual statuses of 'Canceled' or 'Faulted'. I cannot specify the task as long running, however. So, anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?
I have Task that loops continuously until it is externally canceled that I would like to set as 'long running' (i.e. give it a dedicated thread)... anyone know how to best go about running an async task while both storing that active task itself (with desired taskStatus) and setting the task to long running?
There's a few problems with this. First, "long running" does not necessarily mean a dedicated thread - it just means that you're giving the TPL a hint that the task is long-running. In the current (4.5) implementation, you will get a dedicated thread; but that's not guaranteed and could change in the future.
So, if you need a dedicated thread, you'll have to just create one.
The other problem is the notion of an "asynchronous task". What actually happens with async code running on the thread pool is that the thread is returned to the thread pool while the asynchronous operation (i.e., Task.Delay) is in progress. Then, when the async op completes, a thread is taken from the thread pool to resume the async method. In the general case, this is more efficient than reserving a thread specifically to complete that task.
So, with async tasks running on the thread pool, dedicated threads don't really make sense.
Regarding solutions:
If you do need a dedicated thread to run your async code, I'd recommend using the AsyncContextThread from my AsyncEx library:
using (var thread = new AsyncContextThread())
{
Task t = thread.TaskFactory.Run(async () =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
}
});
}
However, you almost certainly don't need a dedicated thread. If your code can execute on the thread pool, then it probably should; and a dedicated thread doesn't make sense for async methods running on the thread pool. More specifically, the long-running flag doesn't make sense for async methods running on the thread pool.
Put another way, with an async lambda, what the thread pool actually executes (and sees as tasks) are just the parts of the lambda in-between the await statements. Since those parts aren't long-running, the long-running flag is not required. And your solution becomes:
Task t = Task.Run(async () =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested(); // not long-running
try
{
"Running...".Dump(); // not long-running
await Task.Delay(500, cts.Token); // not executed by the thread pool
}
catch (TaskCanceledException ex) { }
}
});
Call Unwrap on the task returned from Task.Factory.StartNew this will return the inner task, which has the correct status.
var cts = new CancellationTokenSource();
Task t = Task.Factory.StartNew(
async () => {
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException ex) { }
} }, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap();
On a dedicated thread, there's nothing to yield to. Don't use async and await, use synchronous calls.
This question gives two ways to do a cancellable sleep without await:
Task.Delay(500, cts.Token).Wait(); // requires .NET 4.5
cts.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(500)); // valid in .NET 4.0 and later
If part of your work does use parallelism, you can start parallel tasks, saving those into an array, and use Task.WaitAny on the Task[]. Still no use for await in the main thread procedure.
This is unnecessary and Task.Run will suffice as the Task Scheduler will set any task to LongRunning if it runs for more than 0.5 seconds.
See here why.
https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
You need to specify custom TaskCreationOptions. Let’s consider each of
the options. AttachedToParent shouldn’t be used in async tasks, so
that’s out. DenyChildAttach should always be used with async tasks
(hint: if you didn’t already know that, then StartNew isn’t the tool
you need). DenyChildAttach is passed by Task.Run. HideScheduler might
be useful in some really obscure scheduling scenarios but in general
should be avoided for async tasks. That only leaves LongRunning and
PreferFairness, which are both optimization hints that should only be
specified after application profiling. I often see LongRunning misused
in particular. In the vast majority of situations, the threadpool will
adjust to any long-running task in 0.5 seconds - without the
LongRunning flag. Most likely, you don’t really need it.
The real issue you have here is that your operation is not in fact long running. The actual work you're doing is an asynchronous operation, meaning it will return to the caller basically immediately. So not only do you not need to use the long running hint when having the task scheduler schedule it, there's no need to even use a thread pool thread to do this work, because it'll be basically instantaneous. You shouldn't be using StartNew or Run at all, let alone with the long running flag.
So rather than taking your asynchronous method and starting it in another thread, you can just start it right on the current thread by calling the asynchronous method. Offloading the starting of an already asynchronous operation is just creating more work that'll make things slower.
So your code simplifies all the way down to:
var cts = new CancellationTokenSource();
Task t = DoWork();
async Task DoWork()
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
try
{
"Running...".Dump();
await Task.Delay(500, cts.Token);
}
catch (TaskCanceledException) { }
}
}
I think the consideration should be not how long the thread run but how much of its time is it really working.
In your example there is short work and them await Task.Delay(...).
If this is really the case in your project you probably shouldn't use a dedicated thread for this task and let it run on the regular thread pool. Every time you'll call await on an IO operation or on Task.Delay() you'll release the thread for other tasks to use.
You should only use LongRunning when you'll decrease your thread from the thread-pool and never give it back or give it back only for a small percentage of the time. In such a case (where the work is long and Task.Delay(...) is short in comparison) using a dedicated thread for the job is a reasonable solution.
On the other hand if your thread is really working most of the time it will consume system resources (CPU time) and maybe it doesn't really matter if it holds a thread of the thread-pool since it is preventing other work from happening anyway.
Conclusion? Just use Task.Run() (without LongRunning) and use await in your long running task when and if it is possible. Revert to LongRunning only when you actually see the other approach is causing you problems and even then check your code and design to make sure it is really necessary and there isn't something else you can change in your code.
Related
Edit
I find Building Async Coordination Primitives, Part 1: AsyncManualResetEvent might be related to my topic.
In the case of TaskCompletionSource, that means that synchronous continuations can happen as part of a call to {Try}Set*, which means in our AsyncManualResetEvent example, those continuations could execute as part of the Set method. Depending on your needs (and whether callers of Set may be ok with a potentially longer-running Set call as all synchronous continuations execute), this may or may not be what you want.
Many thanks to all of the answers, thank you for your knowledge and patience!
Original Question
I know that Task.Run runs on a threadpool thread and threads can have re-entrancy. But I never knew that 2 tasks can run on the same thread when they are both alive!
My Question is: is that reasonable by design? Does that mean lock inside an async method is meaningless (or say, lock cannot be trusted in async method block, if I'd like a method that doesn't allow reentrancy)?
Code:
namespace TaskHijacking
{
class Program
{
static TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
static object methodLock = new object();
static void MethodNotAllowReetrance(string callerName)
{
lock(methodLock)
{
Console.WriteLine($"Enter MethodNotAllowReetrance, caller: {callerName}, on thread: {Thread.CurrentThread.ManagedThreadId}");
if (callerName == "task1")
{
tcs.SetException(new Exception("Terminate tcs"));
}
Thread.Sleep(1000);
Console.WriteLine($"Exit MethodNotAllowReetrance, caller: {callerName}, on thread: {Thread.CurrentThread.ManagedThreadId}");
}
}
static void Main(string[] args)
{
var task1 = Task.Run(async () =>
{
await Task.Delay(1000);
MethodNotAllowReetrance("task1");
});
var task2 = Task.Run(async () =>
{
try
{
await tcs.Task; // await here until task SetException on tcs
}
catch
{
// Omit the exception
}
MethodNotAllowReetrance("task2");
});
Task.WaitAll(task1, task2);
Console.ReadKey();
}
}
}
Output:
Enter MethodNotAllowReetrance, caller: task1, on thread: 6
Enter MethodNotAllowReetrance, caller: task2, on thread: 6
Exit MethodNotAllowReetrance, caller: task2, on thread: 6
Exit MethodNotAllowReetrance, caller: task1, on thread: 6
The control flow of the thread 6 is shown in the figure:
You already have several solutions. I just want to describe the problem a bit more. There are several factors at play here that combine to cause the observed re-entrancy.
First, lock is re-entrant. lock is strictly about mutual exclusion of threads, which is not the same as mutual exclusion of code. I think re-entrant locks are a bad idea in the 99% case (as described on my blog), since developers generally want mutual exclusion of code and not threads. SemaphoreSlim, since it is not re-entrant, mutually excludes code. IMO re-entrant locks are a holdover from decades ago, when they were introduced as an OS concept, and the OS is just concerned about managing threads.
Next, TaskCompletionSource<T> by default invokes task continuations synchronously.
Also, await will schedule its method continuation as a synchronous task continuation (as described on my blog).
Task continuations will sometimes run asynchronously even if scheduled synchronously, but in this scenario they will run synchronously. The context captured by await is the thread pool context, and the completing thread (the one calling TCS.TrySet*) is a thread pool thread, and in that case the continuation will almost always run synchronously.
So, you end up with a thread that takes a lock, completes a TCS, thus executing the continuations of that task, which includes continuing another method, which is then able to take that same lock.
To repeat the existing solutions in other answers, to solve this you need to break that chain at some point:
(OK) Use a non-reentrant lock. SemaphoreSlim.WaitAsync will still execute the continuations while holding the lock (not a good idea), but since SemaphoreSlim isn't re-entrant, the method continuation will (asynchronously) wait for the lock to be available.
(Best) Use TaskCompletionSource.RunContinuationsAsynchronously, which will force task continuations onto a (different) thread pool thread. This is a better solution because your code is no longer invoking arbitrary code while holding a lock (i.e., the task continuations).
You can also break the chain by using a non-thread-pool context for the method awaiting the TCS. E.g., if that method had to resume on a UI thread, then it could not be run synchronously from a thread pool thread.
From a broader perspective, if you're mixing locks and TaskCompletionSource instances, it sounds like you may be building (or may need) an asynchronous coordination primitive. I have an open-source library that implements a bunch of them, if that helps.
A task is an abstraction over some amount of work. Usually this means that the work is split into parts, where the execution can be paused and resumed between parts. When resuming it may very well run on another thread. But the pausing/resuming may only be done at the await statements. Notably, while the task is 'paused', for example because it is waiting for IO, it does not consume any thread at all, it will only use a thread while it is actually running.
My Question is: is that reasonable by design? Does that mean lock inside an async method is meaningless?
locks inside a async method is far from meaningless since it allows you to ensure a section of code is only run by one thread at a time.
In your first example there can be only one thread that has the lock at a time. While the lock is held that task cannot be paused/resumed since await is not legal while in a lock body. So a single thread will execute the entire lock body, and that thread cannot do anything else until it completes the lock body. So there is no risk of re-entrancy unless you invoke some code that can call back to the same method.
In your updated example the problem occurs due to TaskCompletionSource.SetException, this is allowed to reuse the current thread to run any continuation of the task immediately. To avoid this, and many other issues, make sure you only hold the lock while running a limited amount of code. Any method calls that may run arbitrary code risks causing deadlocks, reentrancy, and many other problems.
You can solve the specific problem by using a ManualResetEvent(Slim) to do the signaling between threads instead of using a TaskCompletionSource.
So your method is basically like this:
static void MethodNotAllowReetrance()
{
lock (methodLock) tcs.SetResult();
}
...and the tcs.Task has a continuation attached that invokes the MethodNotAllowReetrance. What happens then is the same thing that would happen if your method was like this instead:
static void MethodNotAllowReetrance()
{
lock (methodLock) MethodNotAllowReetrance();
}
The moral lesson is that you must be very careful every time you invoke any method inside a lock-protected region. In this particular case you have a couple of options:
Don't complete the TaskCompletionSource while holding the lock. Defer its completion until after you have exited the protected region:
static void MethodNotAllowReetrance()
{
bool doComplete = false;
lock (methodLock) doComplete = true;
if (doComplete) tcs.SetResult();
}
Configure the TaskCompletionSource so that it invokes its continuations asynchronously, by passing the TaskCreationOptions.RunContinuationsAsynchronously in its constructor. This is an option that you don't have very often. For example when you cancel a CancellationTokenSource, you don't have the option to invoke asynchronously the callbacks registered to its associated CancellationToken.
Refactor the MethodNotAllowReetrance method in a way that it can handle reentrancy.
Use SemaphoreSlim instead of lock, since, as the documentation says:
The SemaphoreSlim class doesn't enforce thread or task identity
In your case, it would look something like this:
// Semaphore only allows one request to enter at a time
private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
void SyncMethod() {
_semaphoreSlim.Wait();
try {
// Do some sync work
} finally {
_semaphoreSlim.Release();
}
}
The try/finally block is optional, but it makes sure that the semaphore is released even if an exception is thrown somewhere in your code.
Note that SemaphoreSlim also has a WaitAsync() method, if you want to wait asynchronously to enter the semaphore.
Given the code below what is the most elegant way to notify consumers when all tasks have run to completion without blocking the thread? Currently I have a solution using a counter which is incremented before the Execute(action) and decremented after the Continuation() and LogException(). If the counter is zero then it is safe to assume that no more tasks are being processed.
First off, don't use the Task constructor at all. If you want to run a delegate in a thread pool thread, use Task.Run. Next, don't use ContinueWith, use await to add continuations to tasks. It's much easier to write correct code that way, particularly with respect to proper error handling. Your code is most effectively written as:
try
{
await Task.Run(() => Execute(action));
Continuation();
}
catch(Exception e)
{
LogExceptions(e)
}
finally
{
CustomLogging();
}
What's the difference between starting and awaiting? Code below taken from Stephen Cleary's blog (including comments)
public async Task DoOperationsConcurrentlyAsync()
{
Task[] tasks = new Task[3];
tasks[0] = DoOperation0Async();
tasks[1] = DoOperation1Async();
tasks[2] = DoOperation2Async();
// At this point, all three tasks are running at the same time.
// Now, we await them all.
await Task.WhenAll(tasks);
}
I thought that the tasks begin running when you await them ... but the comments in the code seem to imply otherwise.
Also, how can the tasks be running after I just attributed them to an array of type Task. Isn't that just an attribution, by nature not involving action?
A Task returns "hot" (i.e. already started). await asynchronously waits for the Task to complete.
In your example, where you actually do the await will affect whether the tasks are ran one after the other, or all of them at the same time:
await DoOperation0Async(); // start DoOperation0Async, wait for completion, then move on
await DoOperation1Async(); // start DoOperation1Async, wait for completion, then move on
await DoOperation2Async(); // start DoOperation2Async, wait for completion, then move on
As opposed to:
tasks[0] = DoOperation0Async(); // start DoOperation0Async, move on without waiting for completion
tasks[1] = DoOperation1Async(); // start DoOperation1Async, move on without waiting for completion
tasks[2] = DoOperation2Async(); // start DoOperation2Async, move on without waiting for completion
await Task.WhenAll(tasks); // wait for all of them to complete
Update
"doesn't await make an async operation... behave like sync, in this example (and not only)? Because we can't (!) run anything else in parallel with DoOperation0Async() in the first case. By comparison, in the 2nd case DoOperation0Async() and DoOperation1Async() run in parallel (e.g. concurrency,the main benefits of async?)"
This is a big subject and a question worth being asked as it's own thread on SO as it deviates from the original question of the difference between starting and awaiting tasks - therefore I'll keep this answer short, while referring you to other answers where appropriate.
No, awaiting an async operation does not make it behave like sync; what these keywords do is enabling developers to write asynchronous code that resembles a synchronous workflow (see this answer by Eric Lippert for more).
Calling await DoOperation0Async() will not block the thread executing this code flow, whereas a synchronous version of DoOperation0 (or something like DoOperation0Async.Result) will block the thread until the operation is complete.
Think about this in a web context. Let's say a request arrives in a server application. As part of producing a response to that request, you need to do a long-running operation (e.g. query an external API to get some value needed to produce your response). If the execution of this long-running operation was synchronous, the thread executing your request would block as it would have to wait for the long-running operation to complete. On the other hand, if the execution of this long-running operation was asynchronous, the request thread could be freed up so it could do other things (like service other requests) while the long-running operation was still running. Then, when the long-running operation would eventually complete, the request thread (or possibly another thread from the thread pool) could pick up from where it left off (as the long-running operation would be complete and it's result would now be available) and do whatever work was left to produce the response.
The server application example also addresses the second part of your question about the main benefits of async - async/await is all about freeing up threads.
Isn't that just an attribution, by nature not involving action?
By calling the async method you execute the code within. Usually down the chain one method will create a Task and return it either by using return or by awaiting.
Starting a Task
You can start a Task by using Task.Run(...). This schedules some work on the Task Thread Pool.
Awaiting a Task
To get a Task you usually call some (async) Method that returns a Task. An async method behaves like a regular method until you await (or use Task.Run() ). Note that if you await down a chain of methods and the "final" method only does a Thread.Sleep() or synchronous operation - then you will block the initial calling thread, because no method ever used the Task's Thread Pool.
You can do some actual asynchronous operation in many ways:
using Task.Run
using Task.Delay
using Task.Yield
call a library that offers asynchronous operations
These are the ones that come to my mind, there are probably more.
By example
Let's assume that Thread ID 1 is the main thread where you are calling MethodA() from. Thread IDs 5 and up are Threads to run Tasks on (System.Threading.Tasks provides a default Scheduler for that).
public async Task MethodA()
{
// Thread ID 1, 0s passed total
var a = MethodB(); // takes 1s
// Thread ID 1, 1s passed total
await Task.WhenAll(a); // takes 2s
// Thread ID 5, 3s passed total
// When the method returns, the SynchronizationContext
// can change the Thread - see below
}
public async Task MethodB()
{
// Thread ID 1, 0s passed total
Thread.Sleep(1000); // simulate blocking operation for 1s
// Thread ID 1, 1s passed total
// the await makes MethodB return a Task to MethodA
// this task is run on the Task ThreadPool
await Task.Delay(2000); // simulate async call for 2s
// Thread ID 2 (Task's pool Thread), 3s passed total
}
We can see that MethodA was blocked on the MethodB until we hit an await statement.
Await, SynchronizationContext, and Console Apps
You should be aware of one feature of Tasks. They make sure to invoke back to a SynchronizationContext if one is present (basically non-console apps). You can easily run into a deadlock when using .Result or .Wait() on a Task if the called code does not take measures. See https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/
async/await as syntactic sugar
await basically just schedules following code to run after the call was completed. Let me illustrate the idea of what is happening behind the scenes.
This is the untransformed code using async/await. The Something method is awaited, so all following code (Bye) will be run after Something completed.
public async Task SomethingAsync()
{
Hello();
await Something();
Bye();
}
To explain this I add a utility class Worker that simply takes some action to run and then notify when done.
public class Worker
{
private Action _action;
public event DoneHandler Done;
// skipping defining DoneHandler delegate
// store the action
public Worker(Action action) => _action = action;
public void Run()
{
// execute the action
_action();
// notify so that following code is run
Done?.Invoke();
}
}
Now our transformed code, not using async/await
public Task SomethingAsync()
{
Hello(); // this remains untouched
// create the worker to run the "awaited" method
var worker = new Worker(() => Something());
// register the rest of our method
worker.Done += () => Bye();
// execute it
worker.Run();
// I left out the part where we return something
// or run the action on a threadpool to keep it simple
}
Here's the short answer:
To answer this you just need to understand what the async / await keywords do.
We know a single thread can only do one thing at a time and we also know that a single thread bounces all over the application to various method calls and events, ETC. This means that where the thread needs to go next is most likely scheduled or queued up somewhere behind the scenes (it is but I won't explain that part here.) When a thread calls a method, that method is ran to completion before any other methods can be ran which is why long running methods are preferred to be dispatched to other threads to prevent the application from freezing. In order to break a single method up into separate queues we need to do some fancy programming OR you can put the async signature on the method. This tells the compiler that at some point the method can be broken up into other methods and placed in a queue to be ran later.
If that makes sense then you're already figuring out what await does... await tells the compiler that this is where the method is going to be broken up and scheduled to run later. This is why you can use the async keyword without the await keyword; although the compiler knows this and warns you. await does all this for you by use of a Task.
How does await use a Task tell the compiler to schedule the rest of the method? When you call await Task the compilers calls the Task.GetAwaiter() method on that Task for you. GetAwaiter() return a TaskAwaiter. The TaskAwaiter implements two interfaces ICriticalNotifyCompletion, INotifyCompletion. Each has one method, UnsafeOnCompleted(Action continuation) and OnCompleted(Action continuation). The compiler then wraps the rest of the method (after the await keyword) and puts it in an Action and then it calls the OnCompleted and UnsafeOnCompleted methods and passes that Action in as a parameter. Now when the Task is complete, if successful it calls OnCompleted and if not it calls UnsafeOnCompleted and it calls those on the same thread context used to start the Task. It uses the ThreadContext to dispatch the thread to the original thread.
Now you can understand that neither async or await execute any Tasks. They simply tell the compiler to use some prewritten code to schedule all of it for you. In fact; you can await a Task that's not running and it will await until the Task is executed and completed or until the application ends.
Knowing this; lets get hacky and understand it deeper by doing what async await does manually.
Using async await
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static async void Test()
{
Console.WriteLine($"Before Task");
await DoWorkAsync();
Console.WriteLine($"After Task");
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
Doing what the compiler does manually (sort of)
Note: Although this code works it is meant to help you understand async await from a top down point of view. It DOES NOT encompass or execute the same way the compiler does verbatim.
using System;
using System.Threading.Tasks;
namespace Question_Answer_Console_App
{
class Program
{
static void Main(string[] args)
{
Test();
Console.ReadKey();
}
public static void Test()
{
Console.WriteLine($"Before Task");
var task = DoWorkAsync();
var taskAwaiter = task.GetAwaiter();
taskAwaiter.OnCompleted(() => Console.WriteLine($"After Task"));
}
static public Task DoWorkAsync()
{
return Task.Run(() =>
{
Console.WriteLine($"{nameof(DoWorkAsync)} starting...");
Task.Delay(1000).Wait();
Console.WriteLine($"{nameof(DoWorkAsync)} ending...");
});
}
}
}
//OUTPUT
//Before Task
//DoWorkAsync starting...
//DoWorkAsync ending...
//After Task
LESSON SUMMARY:
Note that the method in my example DoWorkAsync() is just a function that returns a Task. In my example the Task is running because in the method I use return Task.Run(() =>…. Using the keyword await does not change that logic. It's exactly the same; await only does what I mentioned above.
If you have any questions just ask and I'll be happy to answer them.
With starting you start a task. That means it might be picked up for execution by whatever Multitasaking system is in place.
With waiting, you wait for one task to actually finish before you continue.
There is no such thing as a Fire and Forget Thread. You always need to come back, to react to exceptions or do somethings with the result of the asynchronous operation (Database Query or WebQuery result, FileSystem operation finished, Dokument send to the nearest printer pool).
You can start and have as many task running in paralell as you want. But sooner or later you will require the results before you can go on.
Ok, so basically I have a bunch of tasks (10) and I want to start them all at the same time and wait for them to complete. When completed I want to execute other tasks. I read a bunch of resources about this but I can't get it right for my particular case...
Here is what I currently have (code has been simplified):
public async Task RunTasks()
{
var tasks = new List<Task>
{
new Task(async () => await DoWork()),
//and so on with the other 9 similar tasks
}
Parallel.ForEach(tasks, task =>
{
task.Start();
});
Task.WhenAll(tasks).ContinueWith(done =>
{
//Run the other tasks
});
}
//This function perform some I/O operations
public async Task DoWork()
{
var results = await GetDataFromDatabaseAsync();
foreach (var result in results)
{
await ReadFromNetwork(result.Url);
}
}
So my problem is that when I'm waiting for tasks to complete with the WhenAll call, it tells me that all tasks are over even though none of them are completed. I tried adding Console.WriteLine in my foreach and when I have entered the continuation task, data keeps coming in from my previous Tasks that aren't really finished.
What am I doing wrong here?
You should almost never use the Task constructor directly. In your case that task only fires the actual task that you can't wait for.
You can simply call DoWork and get back a task, store it in a list and wait for all the tasks to complete. Meaning:
tasks.Add(DoWork());
// ...
await Task.WhenAll(tasks);
However, async methods run synchronously until the first await on an uncompleted task is reached. If you worry about that part taking too long then use Task.Run to offload it to another ThreadPool thread and then store that task in the list:
tasks.Add(Task.Run(() => DoWork()));
// ...
await Task.WhenAll(tasks);
If you want to run those task's parallel in different threads using TPL you may need something like this:
public async Task RunTasks()
{
var tasks = new List<Func<Task>>
{
DoWork,
//...
};
await Task.WhenAll(tasks.AsParallel().Select(async task => await task()));
//Run the other tasks
}
These approach parallelizing only small amount of code: the queueing of the method to the thread pool and the return of an uncompleted Task. Also for such small amount of task parallelizing can take more time than just running asynchronously. This could make sense only if your tasks do some longer (synchronous) work before their first await.
For most cases better way will be:
public async Task RunTasks()
{
await Task.WhenAll(new []
{
DoWork(),
//...
});
//Run the other tasks
}
To my opinion in your code:
You should not wrap your code in Task before passing to Parallel.ForEach.
You can just await Task.WhenAll instead of using ContinueWith.
Essentially you're mixing two incompatible async paradigms; i.e. Parallel.ForEach() and async-await.
For what you want, do one or the other. E.g. you can just use Parallel.For[Each]() and drop the async-await altogether. Parallel.For[Each]() will only return when all the parallel tasks are complete, and you can then move onto the other tasks.
The code has some other issues too:
you mark the method async but don't await in it (the await you do have is in the delegate, not the method);
you almost certainly want .ConfigureAwait(false) on your awaits, especially if you aren't trying to use the results immediately in a UI thread.
The DoWork method is an asynchronous I/O method. It means that you don't need multiple threads to execute several of them, as most of the time the method will asynchronously wait for the I/O to complete. One thread is enough to do that.
public async Task RunTasks()
{
var tasks = new List<Task>
{
DoWork(),
//and so on with the other 9 similar tasks
};
await Task.WhenAll(tasks);
//Run the other tasks
}
You should almost never use the Task constructor to create a new task. To create an asynchronous I/O task, simply call the async method. To create a task that will be executed on a thread pool thread, use Task.Run. You can read this article for a detailed explanation of Task.Run and other options of creating tasks.
Just also add a try-catch block around the Task.WhenAll
NB: An instance of System.AggregateException is thrown that acts as a wrapper around one or more exceptions that have occurred. This is important for methods that coordinate multiple tasks like Task.WaitAll() and Task.WaitAny() so the AggregateException is able to wrap all the exceptions within the running tasks that have occurred.
try
{
Task.WaitAll(tasks.ToArray());
}
catch(AggregateException ex)
{
foreach (Exception inner in ex.InnerExceptions)
{
Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source));
}
}
Ok, so basically I have a bunch of tasks (10) and I want to start them all at the same time and wait for them to complete. When completed I want to execute other tasks. I read a bunch of resources about this but I can't get it right for my particular case...
Here is what I currently have (code has been simplified):
public async Task RunTasks()
{
var tasks = new List<Task>
{
new Task(async () => await DoWork()),
//and so on with the other 9 similar tasks
}
Parallel.ForEach(tasks, task =>
{
task.Start();
});
Task.WhenAll(tasks).ContinueWith(done =>
{
//Run the other tasks
});
}
//This function perform some I/O operations
public async Task DoWork()
{
var results = await GetDataFromDatabaseAsync();
foreach (var result in results)
{
await ReadFromNetwork(result.Url);
}
}
So my problem is that when I'm waiting for tasks to complete with the WhenAll call, it tells me that all tasks are over even though none of them are completed. I tried adding Console.WriteLine in my foreach and when I have entered the continuation task, data keeps coming in from my previous Tasks that aren't really finished.
What am I doing wrong here?
You should almost never use the Task constructor directly. In your case that task only fires the actual task that you can't wait for.
You can simply call DoWork and get back a task, store it in a list and wait for all the tasks to complete. Meaning:
tasks.Add(DoWork());
// ...
await Task.WhenAll(tasks);
However, async methods run synchronously until the first await on an uncompleted task is reached. If you worry about that part taking too long then use Task.Run to offload it to another ThreadPool thread and then store that task in the list:
tasks.Add(Task.Run(() => DoWork()));
// ...
await Task.WhenAll(tasks);
If you want to run those task's parallel in different threads using TPL you may need something like this:
public async Task RunTasks()
{
var tasks = new List<Func<Task>>
{
DoWork,
//...
};
await Task.WhenAll(tasks.AsParallel().Select(async task => await task()));
//Run the other tasks
}
These approach parallelizing only small amount of code: the queueing of the method to the thread pool and the return of an uncompleted Task. Also for such small amount of task parallelizing can take more time than just running asynchronously. This could make sense only if your tasks do some longer (synchronous) work before their first await.
For most cases better way will be:
public async Task RunTasks()
{
await Task.WhenAll(new []
{
DoWork(),
//...
});
//Run the other tasks
}
To my opinion in your code:
You should not wrap your code in Task before passing to Parallel.ForEach.
You can just await Task.WhenAll instead of using ContinueWith.
Essentially you're mixing two incompatible async paradigms; i.e. Parallel.ForEach() and async-await.
For what you want, do one or the other. E.g. you can just use Parallel.For[Each]() and drop the async-await altogether. Parallel.For[Each]() will only return when all the parallel tasks are complete, and you can then move onto the other tasks.
The code has some other issues too:
you mark the method async but don't await in it (the await you do have is in the delegate, not the method);
you almost certainly want .ConfigureAwait(false) on your awaits, especially if you aren't trying to use the results immediately in a UI thread.
The DoWork method is an asynchronous I/O method. It means that you don't need multiple threads to execute several of them, as most of the time the method will asynchronously wait for the I/O to complete. One thread is enough to do that.
public async Task RunTasks()
{
var tasks = new List<Task>
{
DoWork(),
//and so on with the other 9 similar tasks
};
await Task.WhenAll(tasks);
//Run the other tasks
}
You should almost never use the Task constructor to create a new task. To create an asynchronous I/O task, simply call the async method. To create a task that will be executed on a thread pool thread, use Task.Run. You can read this article for a detailed explanation of Task.Run and other options of creating tasks.
Just also add a try-catch block around the Task.WhenAll
NB: An instance of System.AggregateException is thrown that acts as a wrapper around one or more exceptions that have occurred. This is important for methods that coordinate multiple tasks like Task.WaitAll() and Task.WaitAny() so the AggregateException is able to wrap all the exceptions within the running tasks that have occurred.
try
{
Task.WaitAll(tasks.ToArray());
}
catch(AggregateException ex)
{
foreach (Exception inner in ex.InnerExceptions)
{
Console.WriteLine(String.Format("Exception type {0} from {1}", inner.GetType(), inner.Source));
}
}