I have some problems with Task.Factory.StartNew and Task.WaitAll. The tasks do start as they should but it looks like it just ignores the Task.WaitAll, because after clicking my button (it's the event this code is in) the MessageBox already popsup.
List<Task> tasks = new List<Task>();
if (plugin.UseProxy)
{
foreach (var item in combo)
{
Task.Factory.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
tasks.Add(t);
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
else
{
foreach (var item in combo)
{
Task.Factory.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
tasks.Add(t);
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Task.WaitAll(tasks.ToArray());
MessageBox.Show("Hello");
You only add the Tasks to your List when they finish. ContinueWith executes when the task is done. So Task.WaitAll is waiting an empty list of Tasks.
So you can do this:
Task task = Task.StartNew(() =>
{
// Some code
}).ContinueWith((t) =>
{
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
tasks.Add(task);
The tasks list only get the items added to it once the thread started via the StartNew finishes. The problem you are having is you are hitting the Task.WaitAll(tasks.ToArray()); before the items are added to the collection. You need to add the items to the collection in the thread that is creating them, not in the continue with,
var newTask = Task.Factory.StartNew(() =>
{
// Some code
});
tasks.Add(newTask);
newTask.ContinueWith((t) =>
{
pbProgress.Value++;
}, TaskScheduler.FromCurrentSynchronizationContext());
However your code has other problems too. You never pass in a TaskSchedueller to the factory, if you don't you can easily accidentally start your thread on the UI thread. Also I assume this code is running on the UI thread, your Task.WaitAll will block the UI thread. This can lead to deadlocks if one of those StartNew threads ended up on a UI thread.
Related
I have a task that performing some heavy work.
I need to path it's result to LogContent
Task<Tuple<SupportedComunicationFormats, List<Tuple<TimeSpan, string>>>>.Factory
.StartNew(() => DoWork(dlg.FileName))
.ContinueWith(obj => LogContent = obj.Result);
This is the property:
public Tuple<SupportedComunicationFormats, List<Tuple<TimeSpan, string>>> LogContent
{
get { return _logContent; }
private set
{
_logContent = value;
if (_logContent != null)
{
string entry = string.Format("Recognized {0} log file",_logContent.Item1);
_traceEntryQueue.AddEntry(Origin.Internal, entry);
}
}
}
Problem is that _traceEntryQueue is data bound to UI, and of cause I will have exception on code like this.
So, my question is how to make it work correctly?
Here is a good article: Parallel Programming: Task Schedulers and Synchronization Context.
Take a look at Task.ContinueWith() method.
Example:
var context = TaskScheduler.FromCurrentSynchronizationContext();
var task = new Task<TResult>(() =>
{
TResult r = ...;
return r;
});
task.ContinueWith(t =>
{
// Update UI (and UI-related data) here: success status.
// t.Result contains the result.
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, context);
task.ContinueWith(t =>
{
AggregateException aggregateException = t.Exception;
aggregateException.Handle(exception => true);
// Update UI (and UI-related data) here: failed status.
// t.Exception contains the occured exception.
},
CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, context);
task.Start();
Since .NET 4.5 supports async/await keywords (see also Task.Run vs Task.Factory.StartNew):
try
{
var result = await Task.Run(() => GetResult());
// Update UI: success.
// Use the result.
}
catch (Exception ex)
{
// Update UI: fail.
// Use the exception.
}
You need to run the ContinueWith -task on the UI thread. This can be accomplished using the TaskScheduler of the UI thread with the overloaded version of the ContinueWith -method, ie.
TaskScheduler scheduler = TaskScheduler.Current;
...ContinueWith(obj => LogContent = obj.Result), CancellationToken.None, TaskContinuationOptions.None, scheduler)
You can use the Dispatcher to invoke code on UI thread. Take a look at the article Working With The WPF Dispatcher
If you are using async/await, then here is some example code that shows how to schedule a task to run on the GUI thread. Place this code at the bottom of the stack of all of your async/await calls to avoid the WPF runtime throwing errors with code not executing on the GUI thread.
Works with WPF + MVVM, tested under VS 2013.
public async Task GridLayoutSetFromXmlAsync(string gridLayoutAsXml)
{
Task task = new Task(() => // Schedule some task here on the GUI thread );
task.RunSynchronously();
await task;
}
UI freeze for 3-10 seconds while update data in UI thread I want to update data in UI thread without freeze.
Code:
Task t = Task.Factory.StartNew(() =>
{
// Get data from Server
GetData(true);
});
Inside Getdata()
//Converst JSON to DataSet Object:- "tempDataSet"
Task task = Task.Factory.StartNew(() =>
{
RetriveData(tempDataSet, firstTime);
}, CancellationToken.None, TaskCreationOptions.None, MainFrame.Current);
Inside RetriveData
DataTable response = tempDataSet.Tables["response"];
DataTable conversations = tempDataSet.Tables["convo"];
foreach (DataRow row in conversations.Rows) // UI Hangs in the method
{
UC_InboxControl control = new UC_InboxControl(row, uC_Inbox);
if (uC_Inbox.mnuUnreadChat.IsChecked == false)
{
inboxControlCollection.Add(control);
}
else
{
inboxUnreadOnlyControlCollection.Add(control);
}
}
What is the best approach to update UI in UI thread without hangs or freeze?
The GetData method should not access any UI elements. It should be executed on a background thread and return a list of objects that you want to display in the view. You could then use the ContinueWith method to populate the ObservableCollection with these objects back on the UI thread, e.g.:
Task t = Task.Factory.StartNew(() =>
{
return GetData(true); // <-- GetData should return a collection of objects
}).ContinueWith(task =>
{
//that you add to your ObservableCollection here:
foreach (var item in task.Result)
yourObservableCollection.Add(item);
},
System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
The same result can be achieved with async/await, which will restore the UI context after completing the task:
// await the task itself, after that do the UI stuff
var collection = await Task.Run(() =>
{
// directly call the retrieve data
return RetriveData(tempDataSet, firstTime);
});
// this code will resume on UI context
foreach (var item in collection)
{
var control = new UC_InboxControl(row, uC_Inbox);
if (!uC_Inbox.mnuUnreadChat.IsChecked)
{
inboxControlCollection.Add(control);
}
else
{
inboxUnreadOnlyControlCollection.Add(control);
}
}
As you can see, I call the RetriveData directly here. Also you can mark it as async too, so you can do:
public async Task<> GetData(...)
{
// some code ...
return await Task.Run(() =>
{
return RetriveData(tempDataSet, firstTime));
}
}
To achieve this you need to mark the method as async. If it is a event handler, you can use async void, in other case use async Task.
I'm trying to properly start a returned Task on a background thread so as not to block the UI thread.
The blocking call is done in another class as such:
var tcs = new TaskCompletionSource<T>();
request.BeginGetResponse(() => DoSomethingSlowThenCompleteTCS(tcs));
return tcs.Task;
I assumed I could simply start the task as such (or a million other variations I've tried:
CallThatReturnsTask()
.ContinueWith(
x =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new System.Action(() =>
{
// Some continuation stuff that needs to be on the dispatcher
}
))).Start();
base.OnActivate(); // Call that needs to run immediately
However I've found that I needed to wrap the returning task in a Task.Run() in order to not block the UI thread. I'm almost 100% certain that doing this defeats the purpose of returning the Task in the first place, but it's the only way I've gotten it working.
Task.Run(() =>
{
CallThatReturnsTask()
.ContinueWith(
x =>
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new System.Action(() =>
{
// Some continuation stuff that needs to be on the dispatcher
}
)));
});
base.OnActivate(); // Call that needs to run immediately
What's the correct way to go about this?
Thanks in advance.
-------------------- Edit 1 --------------------
Is this better? It still seems as if I'm wrapping a task within a task instead of just executing the first task on the correct thread.
Task.Run(() => {
var result = CallThatReturnsTask().Result;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Action(() =>
{
// Dispatcher stuff
}
));
});
I have a task that performing some heavy work.
I need to path it's result to LogContent
Task<Tuple<SupportedComunicationFormats, List<Tuple<TimeSpan, string>>>>.Factory
.StartNew(() => DoWork(dlg.FileName))
.ContinueWith(obj => LogContent = obj.Result);
This is the property:
public Tuple<SupportedComunicationFormats, List<Tuple<TimeSpan, string>>> LogContent
{
get { return _logContent; }
private set
{
_logContent = value;
if (_logContent != null)
{
string entry = string.Format("Recognized {0} log file",_logContent.Item1);
_traceEntryQueue.AddEntry(Origin.Internal, entry);
}
}
}
Problem is that _traceEntryQueue is data bound to UI, and of cause I will have exception on code like this.
So, my question is how to make it work correctly?
Here is a good article: Parallel Programming: Task Schedulers and Synchronization Context.
Take a look at Task.ContinueWith() method.
Example:
var context = TaskScheduler.FromCurrentSynchronizationContext();
var task = new Task<TResult>(() =>
{
TResult r = ...;
return r;
});
task.ContinueWith(t =>
{
// Update UI (and UI-related data) here: success status.
// t.Result contains the result.
},
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, context);
task.ContinueWith(t =>
{
AggregateException aggregateException = t.Exception;
aggregateException.Handle(exception => true);
// Update UI (and UI-related data) here: failed status.
// t.Exception contains the occured exception.
},
CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, context);
task.Start();
Since .NET 4.5 supports async/await keywords (see also Task.Run vs Task.Factory.StartNew):
try
{
var result = await Task.Run(() => GetResult());
// Update UI: success.
// Use the result.
}
catch (Exception ex)
{
// Update UI: fail.
// Use the exception.
}
You need to run the ContinueWith -task on the UI thread. This can be accomplished using the TaskScheduler of the UI thread with the overloaded version of the ContinueWith -method, ie.
TaskScheduler scheduler = TaskScheduler.Current;
...ContinueWith(obj => LogContent = obj.Result), CancellationToken.None, TaskContinuationOptions.None, scheduler)
You can use the Dispatcher to invoke code on UI thread. Take a look at the article Working With The WPF Dispatcher
If you are using async/await, then here is some example code that shows how to schedule a task to run on the GUI thread. Place this code at the bottom of the stack of all of your async/await calls to avoid the WPF runtime throwing errors with code not executing on the GUI thread.
Works with WPF + MVVM, tested under VS 2013.
public async Task GridLayoutSetFromXmlAsync(string gridLayoutAsXml)
{
Task task = new Task(() => // Schedule some task here on the GUI thread );
task.RunSynchronously();
await task;
}
I have a small MVVM application that communicates with a database. What (if any) is the standard way to perform database transactions in a background thread that updates the UI when complete? Should I use BackgroundWorkers, TPL, or implement my own Threads? Currently I have a static class with the following method for background work:
public static void RunAsync(Action backgroundWork, Action uiWork, Action<Exception> exceptionWork) {
var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
// The time consuming work is run on a background thread.
var backgroundTask = new Task(() => backgroundWork());
// The UI work is run on the UI thread.
var uiTask = backgroundTask.ContinueWith(_ => { uiWork(); },
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
uiContext);
// Exceptions in the background task are handled on the UI thread.
var exceptionTask = backgroundTask.ContinueWith(t => { exceptionWork(t.Exception); },
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
uiContext);
// Exceptions in the UI task are handled on on the UI thread.
var uiExceptionTask = uiTask.ContinueWith(t => { exceptionWork(t.Exception); },
CancellationToken.None,
TaskContinuationOptions.OnlyOnFaulted,
uiContext);
backgroundTask.Start();
}
You can use async/await, which will give you a more natural syntax:
public static async Task RunAsync(Action backgroundWork, Action uiWork, Action<Exception> exceptionWork)
{
try
{
// The time consuming work is run on a background thread.
await Task.Run(backgroundWork);
// The UI work is run on the UI thread.
uiWork();
}
catch (Exception ex)
{
// Exceptions in the background task and UI work are handled on the UI thread.
exceptionWork(ex);
}
}
Or better yet, just replace RunAsync with the code itself, so instead of
T[] values;
RunAsync(() => { values = GetDbValues(); }, () => UpdateUi(values), ex => UpdateUi(ex));
You can say:
try
{
var values = await Task.Run(() => GetDbValues());
UpdateUi(values);
}
catch (Exception ex)
{
UpdateUi(ex);
}
Well you can use any of these techniques. I would always run them on a separate thread though. The important thing is that the thread action is marshalled back onto the UI thread at the appropriate time. My preference is to use a task or async await if in .net 4.5