My program is creating some Tasks to do some work.
Now I want to wait for all tasks to complete before I will exit my program.
I know the Task.WaitAll(...) method but in my case I want to create some Tasks dynamically --> they don't exist when Task.WaitAll() is called.
My question now is:
how can I wait for all running Tasks until they are completed?
This is what I'm doing now.
It works but I want to be sure this is also a good way to do it.
public class Test
{
public ConcurrentBag<Task> RunningTasks { get; set; }
public Test()
{
RunningTasks = new ConcurrentBag<Task>();
}
public void RunIt(){
//...create some Tasks asynchronously so they may be created in a few seconds and add them to the list like
//RunningTasks.Add(...);
//Just wait until all tasks finished.
var waitTask = Task.Run(() =>
{
while (true)
{
if (RunningTasks.All(t => t.IsCompleted))
break;
Thread.Sleep(1000);
}
});
waitTask.Wait();
}
}
Task.WhenAll() will take an array of Task, and the ConcurrentBag<T> supports the ToArray() extension method, so you can simply do:
await Task.WhenAll(RunningTasks.ToArray())
or if you don't want to use the await keyword:
Task.WhenAll(RunningTasks.ToArray()).Wait()
UPDATE
So if your RunningTasks is changing after the initial call you can do this pretty easily to handle it:
while(RunningTasks.Any(t=>!t.IsCompleted))
{
Task.WhenAll(RunningTasks.ToArray());
}
The main benefit over your method is that this will yield the thread to other work while it waits, where your code will tie up the thread in a sleep state until all the tasks are completed.
Related
I've been attempting to use SemaphoreSlim to limit the amount of concurrent tasks I have running at any one time but it seems to have no effect, likely down to my implementation which is why I'm here. My SemaphoreSlim code is like so:
First it's called by
await Task.Run(() => mc.StartAsync());
Calling this method
public async Task StartAsync()
{
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(5))
{
foreach (var task in ThreadHandler.ThreadList)
{
await concurrencySemaphore.WaitAsync();
try
{
await Task.Run(() => task.Operation.Start());
}
finally
{
concurrencySemaphore.Release();
}
}
}
}
This in turn is starting a task from a list which looks like this, this is in a custom model with the task stored and created previously
public Task Operation { get; set; }
var t = new Task(async () =>
{
await Task.Run(() => Method(input, sps));
});
Remembering my code doesn't work as I'd expect, is this the correct way to start something like this? I don't expect 3 tasks launching from a single point is a good idea. The main reason It's like this is because I'm executing an Action<> and couldn't figure out a way to await it alone. Do these tasks count towards the SemaphoreSlim limit?
After various tests I can confirm that my SemaphoreSlim code is just continuously executing the tasks, I added a large task delay into the list of tasks to see if I could stop it from executing which worked but new tasks were still launched... what am I missing?
My goal is to have a limit on the number of tasks concurrently running, if that wasn't clear. Thank you for any assistance!
EDIT: I think I've realised I'm only awaiting the starting of the task, not the completion.
I think I've realised I'm only awaiting the starting of the task, not the completion.
Indeed, that is the core of the problem.
You shouldn't use the Task constructor, ever, at all, for anything. Just pretend it doesn't exist. It will always lead you down an awkward path.
If you have an action you want to perform at a later time, you should use a delegate: Action or Func<T> for synchronous work, and Func<Task> or Func<Task<T>> for asynchronous work. E.g., if Method is synchronous, then you would have:
public Action Operation { get; set; }
...
Operation = () => Method(input, sps);
Then you can invoke it using Task.Run as such:
public async Task ProcessAsync()
{
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(5))
{
var tasks = ThreadHandler.ThreadList.Select(async task =>
{
await concurrencySemaphore.WaitAsync();
try
{
await Task.Run(() => task.Operation());
}
finally
{
concurrencySemaphore.Release();
}
}).ToList();
await Task.WhenAll(tasks);
}
}
The above code will work fine if Operation is Action (synchronous) or Func<Task> (asynchronous).
However, if it is Action (i.e., synchronous), then what you're really doing is parallel processing, not asynchronous concurrency, and there's built-in types that can help with that:
public void Process()
{
// Only valid if Operation is Action, not Func<Task>!
Parallel.ForEach(
ThreadHandler.ThreadList,
new ParallelOptions { MaxDegreeOfParallelism = 5 },
task => task.Operation());
}
First, this is from something much bigger and yes, I could completely avoid this using await under normal/other circumstances. For anyone interested, I'll explain below.
To track how many tasks I still have left before my program continues, I've built the following:
A counter:
private static int counter;
Some method:
public static void Test()
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
var task = DoTaskWork();
task.ContinueWith(t => // After DoTaskWork
{
// [...] Use t's Result
counter--; // Decrease counter
tcs.SetResult(null); // Finish the task that the UI or whatever is waiting for
});
tasks.Add(tcs.Task); // Store tasks to wait for
}
Task.WaitAll(tasks.ToArray()); // Wait for all tasks that actually only finish in the ContinueWith
Console.WriteLine(counter);
}
My super heavy work to do:
private static Task DoTaskWork()
{
counter++; // Increase counter
return Task.Delay(500);
}
Now, interestingly I do not receive the number 0 at the end when looking at counter. Instead, the number varies with each execution. Why is this? I tried various tests, but cannot find the reason for the irregularity. With the TaskCompletionSource I believed this to be reliable. Thanks.
Now, for anyone that is interested in why I do this:
I need to create loads of tasks without starting them. For this I need to use the Task constructor (one of its rare use cases). Its disadvantage to Task.Run() is that it cannot handle anything with await and that it needs a return type from the Task to properly run (hence the null as result). Therefore, I need a way around that. Other ideas welcome...
Well. I am stupid. Just 5 minutes in, I realize that.
I just did the same while locking a helper object before changing the counter in any way and now it works...
private static int counter;
private static object locker = new object();
// [...]
task.ContinueWith(t =>
{
lock(locker)
counter--;
tcs.SetResult(null);
});
// [...]
private static Task DoTaskWork()
{
lock (locker)
counter++;
return Task.Delay(500);
}
I need to create loads of tasks without starting them ... Therefore, I need a way around that. Other ideas welcome...
So, if I read it correct you want to build a list of tasks without actually run them on creation. You could do that by building a list of Func<Task> objects you invoke when required:
async Task Main()
{
// Create list of work to do later
var tasks = new List<Func<Task>>();
// Schedule some work
tasks.Add(() => DoTaskWork(1));
tasks.Add(() => DoTaskWork(2));
// Wait for user input before doing work to demonstrate they are not started right away
Console.ReadLine();
// Execute and wait for the completion of the work to be done
await Task.WhenAll(tasks.Select(t => t.Invoke()));
Console.WriteLine("Ready");
}
public async Task DoTaskWork(int taskNr)
{
await Task.Delay(100);
Console.WriteLine(taskNr);
}
This will work, even if you use Task.Run like this:
public Task DoTaskWork(int taskNr)
{
return Task.Run(() =>
{
Thread.Sleep(100); Console.WriteLine(taskNr);
});
}
It this is not want you want can you elaborate more about the tasks you want to create?
I'm making a windows phone game with Unity3d and I have the need to call a method from the Unity thread asynchronously from the UI thread.
This all works, however with one particular method the first execution executes as expected however after the second it seems to lock up the game.
private async static Task<String> ShowDescriptionProductListing()
{
var x = await CurrentApp.LoadListingInformationAsync();
StringBuilder builder = new StringBuilder();
builder.AppendFormat("{0}\n{1}", x.Description,
x.ProductListings.FirstOrDefault().Value);
return builder.ToString();
}
public static void ShowDescrProduct()
{
string x = ShowDescriptionProductListing().Result;
MessageBox.Show(x);
}
I think the line:
var x = await CurrentApp.LoadListingInformationAsync();
Is most likely the culprit, however I'm having a hard time debugging it.
The class which 'holds' that method in unity is like so:
public static class HelperClass
{
public static void ShowDescrProduct()
{
Dispatcherr.InvokeOnUIThread(Tests.ShowDescrProduct); //The method above
}
}
Dispatcherr (Yeah i need to use namespaces haha) just holds two Action properties that I set inside the UI thread.
public void EnterUIThread(Action action)
{
Dispatcher.BeginInvoke(() =>
{
action();
});
}
private void Unity_Loaded()
{
Dispatcherr.InvokeUIThread = EnterUIThread; //One of the actions I just
//mentioned being assigned the above
//method
}
And it's in the EnterUIThread call to Dispatcher.BeginInvoke that it seems to get locked up, only after the first call - which is always successful.
Confusing me slightly to say the least.
Anyone able to give any insight?
Thanks in advance
You're calling Result on the asynchronous operation. This is going to cause the UI thread to block until the asynchronous operation finishes. The asynchronous operation needs to wait for the UI thread to be free so that the continuation to LoadListingInformationAsync can be scheduled in the UI thread.
Both operations are waiting on each other to finish. Deadlock.
You need to not block the UI thread while waiting for this operation to finish. You should await it instead, making ShowDescrProduct and async method.
I have an abstract class called VehicleInfoFetcher which returns information asynchronously from a WebClient via this method:
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID);
I'd like to combine the results of two separate instances of this class, running each in parallel before combining the results. This is done within a third class, CombinedVehicleInfoFetcher (also itself a subclass of VehicleInfoFetcher)
Here's my code - but I'm not quite convinced that it's running the tasks in parallel; am I doing it right? Could it be optimized?
public class CombinedVehicleInfoFetcher : VehicleInfoFetcher
{
public HashSet<VehicleInfoFetcher> VehicleInfoFetchers { get; set; }
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID)
{
// Create a list of parallel tasks to run
var resultTasks = new List<Task<DTOrealtimeinfo>>();
foreach (VehicleInfoFetcher fetcher in VehicleInfoFetchers)
resultTasks.Add(fetcher.getVehicleInfo(stopID, stopID2, timePointLocal));
// run each task
foreach (var task in resultTasks)
await task;
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
var allRealtimeResults = new List<DTOrealtimeinfo>( resultTasks.Select(t => t.Result) );
return combineTaskResults(allRealtimeResults);
}
DTOrealtimeinfo combineTaskResults(List<DTOrealtimeinfo> realtimeResults)
{
// ...
return rtInfoOutput;
}
}
Edit
Some very helpful answers, here is a re-written example to aid discussion with usr below:
public override async Task<object> combineResults()
{
// Create a list of parallel tasks to run
var resultTasks= new List<object>();
foreach (AnotherClass cls in this.OtherClasses)
resultTasks.Add(cls.getResults() );
// Point A - have the cls.getResults() methods been called yet?
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
return new List<object>( resultTasks.Select(t => t.Result) );
}
}
Almost all tasks start out already started. Probably, whatever fetcher.getVehicleInfo returns is already started. So you can remove:
// run each task
foreach (var task in resultTasks)
await task;
Task.WhenAll is faster and has better error behavior (you want all exceptions to be propagated, not just the first you happen to stumble upon).
Also, await does not start a task. It waits for completion. You have to arrange for the tasks to be started separately, but as I said, almost all tasks are already started when you get them. This is best-practice as well.
To help our discussion in the comments:
Task Test1() { return new Task(() => {}); }
Task Test2() { return Task.Factory.StartNew(() => {}); }
Task Test3() { return new FileStream("").ReadAsync(...); }
Task Test4() { return new TaskCompletionSource<object>().Task; }
Does not "run" when returned from the method. Must be started. Bad practice.
Runs when returned. Does not matter what you do with it, it is already running. Not necessary to add it to a list or store it somewhere.
Already runs like (2).
The notion of running does not make sense here. This task will never complete although it cannot be explicitly started.
I'm trying to transition from the Event-based Asynchronous Pattern where I tracked running methods using unique id's and the asynoperationmanager. As this has now been dropped from Windows 8 Apps I'm trying to get a similar effect with Async/Await but can't quite figure out how.
What I'm trying to achieve is something like
private async Task updateSomething()
{
if(***the method is already running***)
{
runagain = true;
}
else
{
await someMethod();
if (runagain)
{
run the method again
}
}
}
The part I'm struggling with is finding out if the method is running. I've tried creating a Task and looking at the status of both that and the .status of the async method but they don't appear to be the correct place to look.
Thanks
UPDATE: This is the current code I use in .net 4 to achieve the same result. _updateMetaDataAsync is a class based on the Event-Based Asynchronous Pattern.
private void updateMetaData()
{
if (_updateMetaDataAsync.IsTaskRunning(_updateMetaDataGuid_CheckAllFiles))
{
_updateMetaDataGuid_CheckAllFiles_Again = true;
}
else
{
_updateMetaDataGuid_CheckAllFiles_Again = false;
_updateMetaDataAsync.UpdateMetaDataAsync(_updateMetaDataGuid_CheckAllFiles);
}
}
private void updateMetaDataCompleted(object sender, UpdateMetaDataCompletedEventArgs e)
{
if (_updateMetaDataGuid_CheckAllFiles_Again)
{
updateMetaData();
}
}
async/await itself is intended to be used to create sequential operations executed asynchronously from the UI thread. You can get it to do parallel operations, but generally the operations "join" back to the UI thread with some sort of result. (there's also the possibility of doing "fire-and-forget" types of asynchronous operations with await but it's not recommended). i.e. there's nothing inherent to async/await to support progress reporting.
You can get progress out of code using async/await; but you need to use new progress interfaces like IProgress<T>. For more info on progress reporting with async/await, see http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx. Migrating to this should just be a matter of calling an IProgress delegate instead of a Progress event.
If you're using a Task you've created, you can check the Task's Status property (or just see Task.IsCompleted if completion is the only state you are interested in).
That being said, await will not "return" until the operation either completes, raises an exception, or cancels. You can basically safely assume that, if you're still waiting on the "await", your task hasn't completed.
SemaphoreSlim queueToAccessQueue = new SemaphoreSlim(1);
object queueLock = new object();
long queuedRequests = 0;
Task _loadingTask;
public void RetrieveItems() {
lock (queueLock) {
queuedRequests++;
if (queuedRequests == 1) { // 1 is the minimum size of the queue before another instance is queued
_loadingTask = _loadingTask?.ContinueWith(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release()
}) ?? Task.Run(async () => {
RunTheMethodAgain();
await queueToAccessQueue.WaitAsync();
queuedRequests = 0; // indicates that the queue has been cleared;
queueToAccessQueue.Release();
});
}
}
}
public void RunTheMethodAgain() {
** run the method again **
}
The added bonus is that you can see how many items are sitting in the queue!