I've recently changed some code to be asynchronous, using the async/await pattern.
This code is now creating an exception:
private async void Refresh(object stateInfo)
{
await Task.Factory.StartNew(HydrateServerPingDtoList);
// more code here
}
private void HydrateServerPingDtoList()
{
// more code here.
// Exception occurs on this line:
this._serverPingDtoList.Add(new ServerPingDto() { ApplicationServer = server });
}
The exception:
This type of CollectionView does not support changes to its
SourceCollection from a thread different from the Dispatcher thread.
_serverPingDtoList is the backing field for a WPF-bound property. Since I thought that async-await preserved the synchronization context, why would I get this error?
await restores the SynchronizationContext within its own async method. It will not propagate it to a background thread that you start via StartNew.
On a side note, StartNew should not be used in async code; I explain why in detail on my blog. You should use Task.Run to execute CPU-bound code.
However, any UI updates (including updates of data-bound properties) should be done on the UI thread, not on a background task. So, assuming that your HydrateServerPingDtoList is actually CPU-bound, you can do this:
private ServerPingDto HydrateServerPingDtoList()
{
// more code here.
return new ServerPingDto() { ApplicationServer = server };
}
private async Task Refresh(object stateInfo)
{
var serverPingDto = await Task.Run(() => HydrateServerPingDtoList());
this._serverPingDtoList.Add(serverPingDto);
// more code here
}
Related
I've been trying to write an MVVM screen for a WPF application, using the async & await keywords to write asynchronous methods for 1. Initially loading data, 2. Refreshing the data, 3. Saving changes and then refreshing. Although I have this working, the code is very messy and I can't help thinking that there must be a better implementation. Can anyone advise on a simpler implementation?
This is a cut-down version of my ViewModel:
public class ScenariosViewModel : BindableBase
{
public ScenariosViewModel()
{
SaveCommand = new DelegateCommand(async () => await SaveAsync());
RefreshCommand = new DelegateCommand(async () => await LoadDataAsync());
}
public async Task LoadDataAsync()
{
IsLoading = true; //synchronously set the busy indicator flag
await Task.Run(() => Scenarios = _service.AllScenarios())
.ContinueWith(t =>
{
IsLoading = false;
if (t.Exception != null)
{
throw t.Exception; //Allow exception to be caught on Application_UnhandledException
}
});
}
public ICommand SaveCommand { get; set; }
private async Task SaveAsync()
{
IsLoading = true; //synchronously set the busy indicator flag
await Task.Run(() =>
{
_service.Save(_selectedScenario);
LoadDataAsync(); // here we get compiler warnings because not called with await
}).ContinueWith(t =>
{
if (t.Exception != null)
{
throw t.Exception;
}
});
}
}
IsLoading is exposed to the view where it is bound to a busy indicator.
LoadDataAsync is called by the navigation framework when the screen is first viewed, or when a refresh button is pressed. This method should synchronously set IsLoading, then return control to the UI thread until the service has returned the data. Finally throwing any exceptions so they can be caught by the global exception handler (not up for discussion!).
SaveAync is called by a button, passing updated values from a form to the service. It should synchronously set IsLoading, asynchronously call the Save method on the service and then trigger a refresh.
There are a few problems in the code that jump out to me:
Usage of ContinueWith. ContinueWith is a dangerous API (it has a surprising default value for its TaskScheduler, so it should really only be used if you specify a TaskScheduler). It's also just plain awkward compared to the equivalent await code.
Setting Scenarios from a thread pool thread. I always follow the guideline in my code that data-bound VM properties are treated as part of the UI and must only be accessed from the UI thread. There are exceptions to this rule (particularly on WPF), but they're not the same on every MVVM platform (and are a questionable design to begin with, IMO), so I just treat VMs as part of the UI layer.
Where the exceptions are thrown. According to the comment, you want exceptions raised to Application.UnhandledException, but I don't think this code will do that. Assuming TaskScheduler.Current is null at the start of LoadDataAsync/SaveAsync, then the re-raising exception code will actually raise the exception on a thread pool thread, not the UI thread, thus sending it to AppDomain.UnhandledException rather than Application.UnhandledException.
How the exceptions are re-thrown. You'll lose your stack trace.
Calling LoadDataAsync without an await. With this simplified code, it'll probably work, but it does introduce the possibility of ignoring unhandled exceptions. In particular, if any of the synchronous part of LoadDataAsync throws, then that exception would be silently ignored.
Instead of messing around with the manual-exception-rethrows, I recommend just using the more natural approach of exception propagation through await:
If an asynchronous operation fails, the task gets an exception placed on it.
await will examine this exception, and re-raise it in a proper way (preserving the original stack trace).
async void methods do not have a task on which to place an exception, so they will re-raise it directly on their SynchronizationContext. In this case, since your async void methods run on the UI thread, the exception will be sent to Application.UnhandledException.
(the async void methods I'm referring to are the async delegates passed to DelegateCommand).
The code now becomes:
public class ScenariosViewModel : BindableBase
{
public ScenariosViewModel()
{
SaveCommand = new DelegateCommand(async () => await SaveAsync());
RefreshCommand = new DelegateCommand(async () => await LoadDataAsync());
}
public async Task LoadDataAsync()
{
IsLoading = true;
try
{
Scenarios = await Task.Run(() => _service.AllScenarios());
}
finally
{
IsLoading = false;
}
}
private async Task SaveAsync()
{
IsLoading = true;
await Task.Run(() => _service.Save(_selectedScenario));
await LoadDataAsync();
}
}
Now all the problems have been resolved:
ContinueWith has been replaced with the more appropriate await.
Scenarios is set from the UI thread.
All exceptions are propagated to Application.UnhandledException rather than AppDomain.UnhandledException.
Exceptions maintain their original stack trace.
There are no un-await-ed tasks, so all exceptions will be observed some way or another.
And the code is cleaner, too. IMO. :)
I have an async codeblock running on the pageload.
The codes run smoothy until you reach capturevalue method where we create a new task.On executing that code block the await just freezes and then the control doesnt come back seems like the code just went to deadlock
protected void Page_Load(object sender, EventArgs e)
{
try
{
var textvalue = GetTextValueFromTask();
txtbox.Text = textvalue.Result;
string ss = "";
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private async Task<string> GetTextValueFromTask()
{
string strReturn = await CaptureValue();
return strReturn;
}
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
return await T;
}
Then I made a small change in Capturevalue method.
private async Task<string> CaptureValue()
{
Thread.Sleep(5000);
Task<string> T = Task.Factory.StartNew<string>(() => "hi");
string ss = T.Result;
return await T;
}
Once I made that change it started working normal.What difference did it make on just fetching the result initially. Please help me Iam a newbee to async
The difference is that second time it doesn't happen any "await" because you waited the task yourself, so await doesn't do anything.
I think you missed the await keyword the first time, here:
var textvalue = await GetTextValueFromTask();
Without it your method GetTextValueFromTask runs synchronously, then it comes into CaptureValue method where await occurs. But the default behaviour of the await is that it tries to capture synchronization context where it was called and to continue the rest of the method in that context, in your example it is WPF synchronization context, which does not allow more than one thread to execute at once. But the continuation cannot proceed, because context is already used by await mechanism.
So, one more time. There is one thread (UI thread), that executes your code up to the last await, which is return await T;, then it yields back to the caller - GetTextValueFromTask, and again to the Page_Load when it gets blocked, because initially you called GetTextValueFromTask synchronously (without await). After that, your operation T completes, and your code tries to continue executing using the initial synchronization context, the WPF one. But it can't, because it is already waiting in the Page_Load.
The link in the comments describes the situation in more detail.
Also consider not using Thread.Sleep in async/await scenarios, because it kills all the "asynchronous" nature of the code. Further reading: link.
Another general piece of advice, which is not directly applicable to your source code, is not to use Task.Factory.StartNew, but rather use Task.Run. Explanation here.
Please use Task.Run() instead of Task.Factory.StartNew()
var T = Task.Run(() => "hi");
It's up to Task.Run to decide how to handle this task.
Also please use .ConfigureAwait(false) in your await calls that do not requires the continuation being done in the awaiter thread context.
I have this code to retrieve data from my database:
public async Task<IEnumerable<Member>> GetAllMembersAsync()
{
var members = Repository.GetAll<Member>();
return await Task.Run(() => members.ToListAsync());
}
For an unknown reason (maybe a bug?), I have to use Task.Run in order to make this work (the repository just returns a DbSet<Member>. If I don't do this, my UI hangs forever.
The problem is that I can't do 2 database operations at the same time this way. If I do this, I get this error:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
As you see, I'm already using await.
Is there any way to solve this so I can actually do 2 database operations at the same time and so they run in order?
EDIT:
Code that calls this:
private async void LoadMembers()
{
try
{
var members = await MemberService.GetAllMembersAsync();
Members = new ObservableCollection<Member>(members);
}
catch (EntityException)
{
// connection lost?
}
}
The answer is no. As the error suggests you can't have 2 concurrent EF operations at the same time. Here's a more thorough answer about that: Does Entity Framework support parallel async queries?
On top of that, you don't need to use Task.Run to solve what seems to be a deadlock issue. Simply use ConfigureAwait to make sure the async operation doesn't need the UI SynchronizationContext to complete on. (also, make sure you don't block on async code with Task.Wait or Task.Result):
public async Task<IEnumerable<Member>> GetAllMembersAsync()
{
return await Repository.GetAll<Member>().ToListAsync().ConfigureAwait(false);
}
private async void LoadMembers()
{
try
{
var members = await MemberService.GetAllMembersAsync();
Members = new ObservableCollection<Member>(members);
}
catch (EntityException)
{
// connection lost?
}
}
I think you are in a deadlock situation because you are not using configureawait(true) properly.
You can do
public async Task<IEnumerable<Member>> GetAllMembersAsync()
{
var members = Repository.GetAll<Member>();
return await Task.Run(() => members.ToListAsync());
}
private async void LoadMembers()
{
try
{
var members = await MemberService.GetAllMembersAsync().ConfigureAwait(true);
Members = new ObservableCollection<Member>(members);
}
catch (EntityException)
{
// connection lost?
}
}
after the operation is complete, and if executed from the GUI thread, the GUI thread will resume.
If I have an application with a synchronous method, is it safe to call an async method as shown below on a UI thread or is there an issue or potential deadlock situation? I know that calling Wait will obviously cause issues, but I feel like this may work out alright.
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
I can successfully run on a UI thread without issue, but I feel as if I may be missing something. I understand that I could use a Task and ContinueWith, but in this example, I would want to wait for the result of the async method before exiting the synchronous method.
Update / Clarification
In the example above, let's assume that the MyMainMethod is an overridden method or a property, etc. and cannot be modified to be async.
Let's look at your code:
public void MyMainMethod(){
var getResult = Task.Run(async () => { await getResultAsync(); }).Result;
myLabel.Text = getResult;
}
Regardless of what's taking place inside getResultAsync, this code is blocking the UI thread when it calls task.Result. In most cases, this is already wrong.
Further, the fact that your getResultAsync is async suggests there's already an async operation inside it. There is no reason to wrap it with Task.Run, unless you perform a mix of CPU- and IO- bound tasks inside getResultAsync. Even then, it may not be necessary (see this for more details).
You can control the await continuation context inside getResultAsync with ConfiureAwait(false), and should do so to avoid deadlocks and redundant context switches, where possible.
So, the code can be reduced to:
public void MyMainMethod(){
var getResult = getResultAsync().Result;
myLabel.Text = getResult;
}
As is, it still blocks the UI. To avoid blocking, you need to make it async. See Async All the Way from Best Practices in Asynchronous Programming by Stephen Cleary.
If it cannot be modified to be async (as clarified in the update to your question), then the above is the best you can get. Indeed, it still may cause a deadlock, depending on what's going on inside getResultAsync, with out without Task.Run. To avoid deadlocks, you should not attempt to access the UI thread with a synchronous call like control.Invoke inside getResultAsync, or await any tasks scheduled on the UI thread with TaskScheduler.FromCurrentSynchronizationContext.
However, usually it is possible and desirable to re-factor the code like this into an async version:
public async Task MyMainMethod(){
var getResult = await getResultAsync();
myLabel.Text = getResult;
}
You would be calling it from a top-level entry point of your app, like a UI event handler:
async void Button_Click(object sender, EventArg e)
{
try
{
await MyMainMethod();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
it better to call your ui update through dispatcher.
Task task = LoadTask();
task.ContinueWith(t =>
Dispatcher.BeginInvoke(() => UpdateUI()));
public async Task LoadTask()
{
Task getdata =
Task.Factory.StartNew(() =>
{
Sleep(3000);
});
await getdata;
return;
}
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!