In short, I have a Task enumerable, and I would like to run each Task within the array in an await fashion. Each Task will perform a slow network operation and from my end I simply need to update the WinForm UI once the task is finished.
Below is the code I'm currently using, however I think this is more of a hack than an actual solution:
private void btnCheckCredentials_Click(object sender, EventArgs e)
{
// GetNetCredentials() is irrelevant to the question...
List<NetworkCredential> netCredentials = GetNetCredentials();
// This call is not awaited. Displays warning!
netCredentials.ForEach(nc => AwaitTask(ValidateCredentials(nc)));
}
public async Task<bool> ValidateCredentials(NetworkCredential netCredential)
{
// Network-reliant, slow code here...
}
public async Task AwaitTask(Task<bool> task)
{
await task;
// Dumbed-down version of displaying the task results
txtResults.Text += task.Result.ToString();
}
2nd line of btnCheckCredentials_Click() warning is shown:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
This actually works the way I wanted to, since I do not want to wait for the operation to complete. Instead I just want to fire away the tasks, and then do something as soon as each one of them finishes.
The Task.WhenAny() or Task.WhenAll() methods do function as I expect, since I would like to know of every task finishing - as soon as it finishes. Task.WaitAll() or Task.WaitAny() are blocking and therefore undesirable as well.
Edit: All tasks should start simultaneously. They may then finish in any order.
Are you looking for Task.WhenAll?
await Task.WhenAll(netCredentials.Select(nc => AwaitTask(ValidateCredentials(nc)));
You can do all the completion processing you need in AwaitTask.
The await task; is a bit awkward. I'd do it like this:
public async Task AwaitTask(netCredential credential)
{
var result = await ValidateCredentails(credential);
// Dumbed-down version of displaying the task results
txtResults.Text += result.ToString();
}
You can do this by using Task.WhenAny marking it as async (async void here is fine since you're inside an event handler):
private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
// GetNetCredentials() is irrelevant to the question...
List<NetworkCredential> netCredentials = GetNetCredentials();
var credentialTasks = netCredentials
.Select(cred => ValidateCredentialsAsync(cred))
.ToList();
while (credentialTasks.Count > 0)
{
var finishedTask = await Task.WhenAny(credentialTasks);
// Do stuff with finished task.
credentialTasks.Remove(finishedTask);
}
}
You can fire and forget each task and add callback when task is completed.
private async void btnCheckCredentials_Click(object sender, EventArgs e)
{
List<NetworkCredential> netCredentials = GetNetCredentials();
foreach (var credential in netCredentials)
{
ValidateCredentails(credential).ContinueWith(x=> ...) {
};
}
}
So instead of labda expression you can create callback method and know exactly when the particular task finished.
Related
I've a method which check if a directory exists or not.
public static bool FileExists(string path, int timeout = 500)
{
Func<bool> func = () => File.Exists(path);
using (Task<bool> task = new Task<bool>(func))
{
task.Start();
return task.Wait(timeout) && task.Result;
}
}
It works fine but when I close my wpf application, sometimes I had this exception:
A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled)
Any hints how to detect the exception? I tried to wrap my code into try...catch block but I think there's more elegand solution,
There is an overload of the Wait method accepts a CancellationToken. If you use this one, you could cancel it using a CancellationTokenSource as described in the docs.
You don't really need to dispose the task though:
public static bool FileExists(string path, int timeout = 500)
{
Func<bool> func = () => File.Exists(path);
Task<bool> task = Task.Run(func);
return task.Wait(timeout) && task.Result;
}
Stephan Toub explains more about this in his blog post:
Do I need to dispose of Tasks?
I am creating a WPF application to collect logs. I am using different Backgroundworker to execute some scripts. Worker1 for script1, Worker2 for script2 and so on. When Backgroundworker completes RunworkerCompleted method get executed.
So, how can I wait for all the workers to complete? Once all these workers are complete, I need to update the UI thread.
You can try using Tasks.
Task t1 = Task.Run(() =>
{
// Do something
}
);
Task t2 = Task.Run(() =>
{
// Do something
}
);
Task t3 = Task.Run(() =>
{
// Do something
}
);
await Task.WhenAll(new Task[] { t1, t2, t3 }).ContinueWith((task) =>
{
// Do something when all three tasks have completed...
}
);
Here is a way to wait for all BackgroundWorker objects to get complete. I hope you are using C#:
static void Main(string[] args)
{
int noOfbackgroundWorker = 5;
WaitHandle[] waitHandles = new WaitHandle[noOfbackgroundWorker];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < noOfbackgroundWorker; i++)
{
var bg1 = new BackgroundWorker();
var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
bg1.DoWork += delegate(object sender, DoWorkEventArgs e) { Thread.Sleep(i * 1000); };
bg1.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) { handle.Set(); };
waitHandles[i] = handle;
bg1.RunWorkerAsync();
}
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Done in "+sw.Elapsed.Seconds + " seconds");
Console.ReadKey();
}
Apart from above if there is low no of BackgroundWorker, just set a bool variable for each worker to map each worker complete status and call a common method from all RunworkerCompleted event handler that just has condition if(a && b && c.....) if passed then notify the UI.
As I say in my Task.Run vs BackgroundWorker conclusion:
anything like waiting for two separate background operations to complete before doing something else is much easier with Task.Run. Pretty much any time you have to coordinate background operations, Task.Run code is going to be much simpler!
(this is the conclusion of a series of blog posts comparing Task.Run to BackgroundWorker).
Worker1 for script1, Worker2 for script2 and so on.
You can do this with Task.Run:
Result DoWork(Script script) { ... }
IEnumerable<Script> scripts = ...;
var tasks = scripts.Select(x => Task.Run(() => DoWork(x))).ToList();
So, how can I wait for all the workers to complete? Once all these workers are complete, I need to update the UI thread.
Use await and Task.WhenAll:
var results = await Task.WhenAll(tasks);
... // Update UI with results
I have a code that needs to refresh the UI then wait for it to finish (refreshing may involve animations) and then keep going. Is there a way to call the Application.Current.Dispatcher.Invoke(new Action (()=> { PerformUpdateWithAnimations(); } in a syncrhonous way?
This is the overall look:
List<thingsThatMove> myThings = new List<ThingsThatMove>();
//normal code interacting with the data
// let's name this part of code A
foreach (thing t in myThings)
{
thing.currentPosition = SomePoint;
if(thing.wasRejectedBySystem) thing.needsToMove = true;
}
//As a result of A we have some impact in the UI
//That may need some animations (let's call this bloc B)
Application.Current.Dispatcher.Invoke(new Action(() => {
foreach(thing in myThings)
if(thing.needsToMove)
createNewAnimation(thing);
}));
//Here is some final code that needs the final position of some
//of the elements, so it cannot be executed until the B part has
// been finished. Let's call this bloc C
updateInternalValues(myThings);
cleanUp();
I tried encapsulating B into a BackgroundWoker. Setting the B bloc as the DoWork and listening to the completed but it doent work since the B blok "completes" after the Application.Current.Dispatcher is called, not after the dispatcher itself finishes everything
How can I make C wait until all the animations in B are finished ?
You can use async/await to call asynchrnously and continue after it has been executed..
private async void RefreshCode()
{
List<thingsThatMove> myThings = new List<ThingsThatMove>();
//normal code interacting with the data
// let's name this part of code A
foreach (thing t in myThings)
{
thing.currentPosition = SomePoint;
if(thing.wasRejectedBySystem) thing.needsToMove = true;
}
//As a result of A we have some impact in the UI
//That may need some animations (let's call this bloc B)
await Application.Current.Dispatcher.InvokeAsync(new Action(() => {
foreach(thing in myThings)
if(thing.needsToMove)
createNewAnimation(thing);
}));
//Here is some final code that needs the final position of some
//of the elements, so it cannot be executed until the B part has
// been finished. Let's call this bloc C
updateInternalValues(myThings);
cleanUp();
}
We are working on a windows application which caters to an engineering calculation which are essentially very long running. So we are basically trying to keep the calculation module separate and working in a separate worker thread and pass it an Action delegate in method signature which will be invoked to report the calculation progress in the UI. The delegate handler declared in the UI will be updating the UI. We found that while a huge loop is running in the calculation, the UI is not showing the periodic progress and only displaying the final result. If a Thread Sleep for 1 millisecond is introduced in the calculation loop, the UI is getting updated correctly. This is not expected behavior as we are executing the calculation using a separate Task and updating the UI using BeginInvoke calls.
I have created a simple application to demonstrate our approach and code so that it is easier to understand. It is obvious that we are missing something very simple but cannot quite pin it down. Will appreciate any insights.
Thanks for reading.
private void cmdStart_Click(object sender, EventArgs e)
{
txtResultDIsplay.Text = "";
var maxIterations = long.Parse(txtIterationNo.Text.Trim());
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<double> calculationTask = Task.Factory.StartNew<double>(
() => SumRootN(maxIterations, UpdateProgress));
var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
}
private void DisplayResult(Task<double> calculationTask)
{
txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
}
private void UpdateProgress(string msg)
{
this.BeginInvoke((MethodInvoker)delegate
{
txtResultDIsplay.Text = msg;
});
}
public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
{
int root = 20;
double result = 0;
for (long i = 1; i < maxIterations; i++)
{
Thread.Sleep(1);
result += Math.Exp(Math.Log(i) / root);
progressUpdateDelegate(result.ToString("0.00000"));
}
return result;
}
It is possible you are flooding the UI thread with your progress updates. You need to find a way to prevent lots of updates occurring.
We can solve the problem using tasks!
Task progressTask = null;
private void UpdateProgress(string msg)
{
//only schedule work if the task if not running
if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
{
//Create a task representing the update
progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
.ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
}
}
Note that locking will not do here as you want to skip the update if there is already an update occurring.
I have a query for getting a list of prescriptions as below:
var PRSCRPTSQuery = GV.dbContext.Load(GV.dbContext.GetPRSCRPTQuery(GV.curCustomer.CustCode,
oOrdritemEdited.ProdCode, oOrdritemEdited.MedCode));
PRSCRPTSQuery.Completed += new EventHandler(PRSCRPTSQuery_Completed);
In the query completed event, I have the following code :
void PRSCRPTSQuery_Completed(object sender, EventArgs e)
{
lstPRSCRPT = GV.dbContext.PRSCRPTs.Where(p=>p.Status =="Activated").ToList();
if (lstPRSCRPT.Count > 0)
{
foreach (var rec in lstPRSCRPT)
{
var OrderItemQuery = GV.dbContext.Load(GV.dbContext.GetOrdritemsQuery(rec.PresNo));
OrderItemQuery.Completed += new EventHandler(OrderItemQuery_Completed);
}
}
}
The list lstPRSCRPT can contain more than one record. I presume, the foreach loop will advance to the next item in the loop without waiting for the OrderItemQuery_Completed event which is below:
void OrderItemQuery_Completed(object sender, EventArgs e)
{
lstOrderItem = GV.dbContext.OrderItems.ToList();
if (lstOrderItem.Count > 0)
{
foreach (var OrdrItemRec in lstOrderItem)
{
TotTonnes = (double)(TotTonnes + OrdrItemRec.Quantity);
}
}
}
Is there any work around for this situation? I am new to the asynchronous type of programming in SL
I see where your coming from and when i first started Silverlight programming i gripped to my preconceptions of synchronous execution so i know what i have when ive finished calling a query and also i know exactly where it's errored.
Silverlight however takes this concept and tries to rip it from you yelling "This way is better trust me!" and for the purpose it serves of enriching client side interactivity it certainly succeeds. It just takes time. You just need to learn more about the style of how to link it all together.
The link previously shown by Faster Solutions shows where C# is going in terms of asynchronous coding but it pays to know what its actually accomplishing for you. Some of which you've already grasped in the code you've linked in the question.
When i've faced the same situation you have where you have back to back async callbacks is to raise an event when i've finished doing what i'm doing. For example:
public event EventHandler<EventArgs> LoadComplete;
public int QueryCount {get;set;}
public int QuerysCompleted {get;set;}
public void GetItems()
{
var PRSCRPTSQuery = GV.dbContext.Load(GV.dbContext.GetPRSCRPTQuery
(GV.curCustomer.CustCode, oOrdritemEdited.ProdCode, oOrdritemEdited.MedCode));
PRSCRPTSQuery.Completed += new EventHandler(PRSCRPTSQuery_Completed);
LoadComplete += loader_LoadComplete;
}
void PRSCRPTSQuery_Completed(object sender, EventArgs e)
{
lstPRSCRPT = GV.dbContext.PRSCRPTs.Where(p=>p.Status =="Activated").ToList();
if (lstPRSCRPT.Count > 0)
{
QueryCount = lstPRSCRPT.Count;
foreach (var rec in lstPRSCRPT)
{
var OrderItemQuery = GV.dbContext.Load(GV.dbContext.GetOrdritemsQuery(rec.PresNo));
OrderItemQuery.Completed += new EventHandler(OrderItemQuery_Completed);
}
}
}
void OrderItemQuery_Completed(object sender, EventArgs e)
{
QueryCompleted++;
lstOrderItem = GV.dbContext.OrderItems.ToList();
if (lstOrderItem.Count > 0)
{
foreach (var OrdrItemRec in lstOrderItem)
{
TotTonnes = (double)(TotTonnes + OrdrItemRec.Quantity);
}
}
if(QueryCompleted == QueryCount)
{
RaiseLoadComplete();
}
}
public void RaiseLoadComplete()
{
if(LoadComplete != null)
{
LoadComplete(this, new EventArgs());
}
}
void loader_LoadComplete(object sender, EventArgs e)
{
//Code to execute here
}
I attach an event when starting the first query of what code to execute when i'm done. In the first query call back i initialise a count of how many responses i am expecting. Then in the second query callback i increment until i get the right amount and call the event to say im done.
The only caution with this approach is if one of the queries error, The final code will never get executed.
You might find that the VS Async CTP of interest. It introduces the new "async" keyword for handling asyncronous events. He's a blog explaining it: VS Async