I can return anything back to the UI thread like this:
// when I am on the UI thread
var _UI = System.Threading.SynchronizationContext.Current;
// then on a background thread
_UI.Post((x) => { DoSomething(); }, null);
But, if I am on that background thread for to make it asynchrnous, then calling _UI.Post() will undo my efforts and makes the UI wait for the execution of DoSomething().
Of course _UI.PostAsync() is pseudo-code. But is there a way to execute on the UI thread but in an asynchronous way? Fundamentally, I realize I am exposing my lack of knowlege and understanding. Be merciful, please. I'm just asking ;)
SynchronizationContext.Post() will not block the background thread, as it asynchronously "posts" back to the UI thread (in this case).
The UI thread will only block for the duration of the DoSomething() call - but in this case, you're saying you must run that on the UI thread, in which case, it will tie up the UI thread.
The key here is to put most of your work on the background thread, and only call .Post(...) for operations that directly set the user interface. As such, they should be very fast operations, and never (noticably) block the UI.
Related
I'm working on some old code that runs on custom background thread. this thread uses the thread class and passes it a thread delegate i.e. an action.
_thread = new Thread(() => processData());
i need to call some newer functions that return task so do i just do
myfunc().GetAwaiter().GetResult();
or is there some other way? because since it is a custom thread, i don't think if it is really doing anything this time. how can i call async await from. the custom thread so that it is utilized properly?
If you want to continue using your custom Thread, then yes, it has to block on asynchronous code. GetAwaiter().GetResult() is probably your best bet for that.
or is there some other way?
Yes. If you can replace your custom Thread with a thread pool thread, then you can use Task.Run instead of Thread, and then you can use the more natural await instead of GetAwaiter().GetResult(). Most custom threads can be replaced by thread pool threads, but this is not always the case.
Given this code:
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(() =>
{
//Code to run on UI thread
});
//Code to run on a background thread
}).
Is it safe to assume that "Code to run on a background thread" will not be reached under any circumstances until "Code to run on UI thread" is finished executing?
When you start the Task, it will run as an worker thread and it will block until Invoke(...) is over.
When Invoke(..) is done it will continue on the worker thread.
"Code to run on UI thread" will run first, next will be "Code to run on a background thread".
You then have the possibility to do some work in a worker thread like read files, write files, query on database with out blocking the main thread, and when needed you can update UI elements with data via Invoke(...)
But take a look at Async/Await, it can make async operation and updating UI element easier to understand.
Dispatcher.Invoke Method (Delegate, Object[])
Executes the specified delegate with the specified arguments synchronously on the thread the Dispatcher is associated with.
https://msdn.microsoft.com/en-us/library/cc647509(v=vs.110).aspx
also see this:
Asynchronous vs synchronous execution, what does it really mean?
Because Dispatcher.Invoke is executed synchronously, and how synchronous execution is defined, your assumption is safe.
I have found plenty of resources for how to invoke a function on the UI thread, but I have some logic that is only allowed to be run from the Main Thread. Is there a way to grab a dispatcher on the Main thread and invoke on it?
The "dispatcher" is a concept specific to a particular UI framework (here: WPF). There is no dispatcher you can use to target any thread. Imagine the following thread:
while (true) Console.WriteLine("x");
How are you going to invoke something on that thread? It can't be done because that thread is forever busy doing something else. It is not cooperating.
I kind of doubt that you need to invoke something on the "main thread". But I'll answer the question literally. You need to make the main thread cooperate and accept work from other threads. Maybe a queue of Action or a boolean flag that tells that thread to do something specific.
You can use a combination of signaling + data structures. Define a variable to hold details of a required function call (maybe a struct with parameters), and have your main thread periodically test if a call is required. Make sure to lock necessary objects the handle multi-threading pitfalls. You can also have a signaling object and have the initiator Monitor.Wait on it and the main thread will signal when the function is done.
Edit: The program the OP questioned about did in fact have separate UI and Main threads...
You can create new threads/tasks anytime you want using
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
Task.Factory.StartNew(() =>
{
doSomeWork();
}, token);
The token is used so you can cancel the task. If something goes wrong or the task hangs, you can cancel it. You may have already read about TPL libraries but if not, do and see if it works for what you are wanting to do.
To make my answer a little more complete I wanted to add this...I am not sure this may or may not work in your case but in normal cases you would do something like this to update or work with objects in the main thread from the worker thread.
private void doSomeWork()
{
// do work here -->
if (someObject.InvokeRequired)
{
someObject.BeginInvoke((Action)delegate() { someObject.Property = someValue; });
}
else
{
someObject.Property = someValue;
}
}
At many blogs, tutorials and MSDN I can read that accessing UI elements from non-UI threads is impossible - ok, I'll get an unauthorized exception. To test it I've written a very simple example:
// simple text to which TextBlock.Text is bound
private string sample = "Starting text";
public string Sample
{
get { return sample; }
set { sample = value; RaiseProperty("Sample"); }
}
private async void firstButton_Click(object sender, RoutedEventArgs e)
{
await Job(); // asynchronous heavy job
commands.Add("Element"); // back on UI thread so this should be ok?
}
private async Task Job()
{
// I'm not on UI Thread ?
await Task.Delay(2000); // some other job
Sample = "Changed"; // not ok as not UI thread?
commands.Add("Element from async"); // also not ok?
}
I've a Task which is being run asynchronously. In that Task I want to change my property (which will raise PropertyChanged) and add element to ObservableCollection. As it is run async, I shouldn't be able to do that, but I get no exception and the code is working fine. Thus my doubts and misunderstanding:
why don't I get an exception?
is it ok to raise PropertyChanged in async Task?
is it ok to modify ObservableCollection in async Task, or should I return Task<ICollection> and after obtaining the result modify the ObservableCollection- Clear it and Fill it?
when am I in Task on UI thread and when not?
in the code above in firstButton_Click is it ok to manage UI elements after awaiting the Task? Am I always back on UI thread?
To test it more I've put my property change and collection modification in other thread:
System.Threading.Timer newThreadTimer = new System.Threading.Timer((x) =>
{
Sample = "Changed"; // not UI thread - exception
commands.Add("Element from async"); // not UI thread - exception
}, null, 1000, Timeout.Infinite);
In above code my thinking is ok - just after the first or second line I get an exception. But what with the first code? Is it only a luck that my Taskwas run on UI thread?
I suspect that this is very basic thing and my misunderstanding, but I need some clarification and thus this question.
When awaiting on a Task, the SynchronizationContext of the current thread is captured (specifically in the case of Task by the TaskAwaiter). The continutation is then marshaled back to that SynchronizationContext to execute the rest of the method (the part after the await keyword).
Lets look at your code example:
private async Task Job()
{
// I'm not on UI Thread ?
await Task.Delay(2000); // some other job
Sample = "Changed"; // not ok as not UI thread?
commands.Add("Element from async"); // also not ok?
}
When you await Task.Delay(2000), the compiler implicitly captures the SynchronizationContext, which is currently your WindowsFormsSynchronizationContext. When the await returns, the continuation is executed in the same context, since you didn't explicitly tell it not to, which is your UI thread.
If you changed your code to await Task.Delay(200).ConfigureAwait(false), the continuation would not be marshalled back to your current SynchronizationContext, and would run a ThreadPool thread, causing your UI element update to throw an exception.
In your timer example, the Elapsed event is raised via a ThreadPool thread, hence why you get an exception that you are trying to update an element which is controlled by a different thread.
Now, let's go over your questions one by one:
why don't I get exception?
As said, the await Task.Delay(2000) executed the Continuation on the UI thread, which made it possible to update your controls.
is it ok to Raise properties in async Task?
I am not sure what you mean by "Raise properties", but if you mean raise a INotifyPropertyChanged event, then yes, it is ok to execute them not in a UI thread context.
is it ok to modify ObservableCollecition in async Task, or should I
return Task and after obtaining the result modify Observable - Clear
it and Fill it?
If you have an async method and you want to update a UI bound element, make sure you marshal the continuation on the UI thread. If the method is called from the UI thread and you await its result, then the continuation will implicitly be ran on your UI thread. In case you want to offload work to a background thread via Task.Run and make sure your continuation is ran on the UI, you can capture your SynchronizationContext using TaskScheduler.FromCurrentSynchronizationContext() and explicitly pass it the continuation
when am I in Task on UI thread and when not?
A Task is a promise of work that will be done in the future. when you await on a TaskAwaitable from the UI thread context, then you are still running on the UI thread. You are not in the UI thread if:
Your async method is currently executing from a thread different then the UI thread (either a ThreadPool thread or a new Thread)
You offload work to a background ThreadPool thread using Task.Run.
in the code above in firstButton_Click is it ok to manage UI elements
after awaiting the Task? Am I always back on UI thread?
You will be back to the UI thread as long as you don't explicitly tell your code not to return to its current context using ConfigureAwait(false)
The PropertyChanged event is automatically dispatched to the UI thread by WPF so you can modify properties that raise it from any thread. Before .NET 4.5 this was not the case for ObservableCollection.CollectionChanged event and thus, adding or removing elements from a thread other than the UI one, would cause an exception.
Starting in .NET 4.5 CollectionChanged is also automatically dispatched to the UI thread so you don't need to worry about that. This being said, you'll still need to access UI elements (such as a button for instance) from the UI thread.
We have an application built according to the MVVM pattern. At various times we kick off tasks to go to a database to retrieve data, we then populate an ObservableCollection to which a WPF control is bound with that data.
We are a little confused, when we populate the ObservableCollection we are doing so on the task thread, not the UI thread, yet the UI is still updated/behaving correctly. We were expecting an error and to have to change the code to populate the collection on the UI thread.
Is this a dangerous scenario and should we populate on the UI thread anyway?
Code to get data:
Task.Factory.StartNew(() =>
UiDataProvider.RefreshForwardContractReport(fcrIdField.Value)
)
.ContinueWith(task => {
if (task.Result.OperationSuccess)
{
// This updates the ObseravableCollection, should it be run on UI thread??
RefreshReport(task.Result.OperationResult);
}
});
WPF keeps allowing more cross-thread operations with each release. Other MVVM platforms do not allow them at all. In the case of ObservableCollection, unless you are using EnableCollectionSynchronization, updating from a background thread is incorrect.
I agree with #avo in that you should treat your ViewModel (your logical UI) as though it had UI thread affinity (like the literal UI). All data binding updates should be done in the UI context.
As #Cameron pointed out, this is most easily done via async:
var result = await Task.Run(
() => UiDataProvider.RefreshForwardContractReport(fcrIdField.Value));
if (result.OperationSuccess)
{
RefreshReport(result.OperationResult);
}
There could be any number of reasons why the continuation is running on the UI thread. The MVVM framework could be helping, or something else is making it run on the UI thread, or you're just getting lucky.
To ensure the continuation runs on the UI thread you can capture the UI TaskScheduler right before like so.
var uiScheduler = TaskScheduler.FromCurrentSyncronizationContext();
Task.Factory.StartNew(() =>
UiDataProvider.RefreshForwardContractReport(fcrIdField.Value),
TaskCreationOptions.LongRunning
) // Ensures the task runs in a new thread
.ContinueWith(task => {
if (task.Result.OperationSuccess)
{
RefreshReport(task.Result.OperationResult);
}
}, uiScheduler); // Runs the continuation on the UI thread.
This assumes that the outer method is run from the UI to begin with. Otherwise you could capture the UI scheduler at a top level and access it globally in your app.
If you can use async/await then the code becomes much easier.
var result = await Task.Factory.StartNew(
() => UiDataProvider.RefreshForwardContractReport(fcrIdField.Value),
TaskCreationOptions.LongRunning
);
if (result.OperationSuccess)
{
RefreshReport(result.OperationResult);
}
It happens because WPF automatically dispatches the PropertyChanged event to the main thread, unlike all other XAML frameworks. In all other frameworks, a dispatching solution is needed. The best article I've read about MVVM and multi threading
https://msdn.microsoft.com/en-us/magazine/dn630646.aspx
Considering the MVVM scenario, when you are in the ContinueWith part, i agree that you are in the Non-UI thread and you are updating the properties in the ViewModel (which are bound to UI elemnts) and not the UI elements itself.
Try updating the UI elements in the ContinueWith part, say like
.ContinueWith(task => myTextblock.Text = "SomeText")
In that scenario, u'll get the exception that you are expecting.
P.S - "myTextBlock" is a text block in your View.
You should use another (not UI) thread to retrieve long data from DB or another source for prevent UI frozen. But when you try to change UI element from not UI thread it may throw some exception. To change UI from not UI thread you should add update task to UI thread:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
some udate action here
});
If you only change your date in the ViewModel, there should be no problem.
The ui updates will be raised using INotifyPropertyChanged in your view model.
I prefer the writing of async and await instead of continue with. For most collegues it is more readable. But it is only code sugering, so there will be no different to you implementation.
I believe you should treat your ViewModel in the same way you treat the UI itself. That is, don't modify it directly from a non-UI thread. INotifyPropertyChanged will not automatically do the magic of marshaling from the worker thread to the UI thread, where controls can be updated.
Related questions:
INotifyPropertyChanged with threads
INotifyPropertyChanged causes cross-thread error
add an event to your class as a property and use the background thread (dispatcher) to do your treatment. Once it ends,you invoke the event to upDate the window (the UI thread).
You can also use the background worker Their are small threads used to execute code in a small threads and updates UI after finishing .