I have created a Windows 8 Metro App based on the Split Page sample app. However, in the sample app the data is loaded synchronously in the constructor. I'm accessing a text file and therefore need to load the data asynchronously. The constructor looks like this:
public MyDataSource()
{
DataLoaded = false;
LoadData();
}
LoadData() is an asynchronous method that populates the data model. This works fine, and displays the data as is loads it (which is the behavior that I want). The problem occurs when I try testing the suspend and terminate. The problem being that the recovery has the potential to attempt to access the data model before it is populated:
public static MyDataGroup GetGroup(string uniqueId)
{
// If the data hasn't been loaded yet then what?
if (_myDataSource == null)
{
// Where app has been suspended and terminated there is no data available yet
}
// Simple linear search is acceptable for small data sets
var matches = _myDataSource.AllGroups.Where((group) => group.UniqueId.Equals(uniqueId));
if (matches.Count() == 1) return matches.First();
return null;
}
I can fix this by changing the constructor to call LoadData().Wait, but this means that the app locks the UI thread. What I believe I need is a method of getting the recovery code in GetGroup to wait until the data has loaded without locking the UI thread. Is this possible or advisable, and if so, how?
EDIT:
One or two people have suggested caching the task for LoadData(). This is an excellent idea, but the code inside GetGroup is called by the Page State Management section and therefore cannot be async. To get around this, I tried the following:
if (!DataLoaded)
{
//dataLoading = await MyDataSource.LoadData();
dataLoading.RunSynchronously();
}
But this gives me an error:
RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.
and
dataLoading.Wait()
just locks the UI.
I think that this sounds like the best option would be if you made the constructor async. But since that's not possible, what you can do instead is to create an async factory method for MyDataSource:
private MyDataSource()
{
DataLoaded = false;
}
public static async Task<MyDataSource> Create()
{
var source = new MyDataSource();
await source.LoadData();
return source;
}
And then initialize _myDataSource using await:
_myDataSource = await MyDataSource.Create();
If, for some reason, you can't do that, you can store the Task returned by the factory method and wait for it in GetGroup():
_myDataSourceTask = MyDataSource.Create();
…
public static async Task<MyDataGroup> GetGroup(string uniqueId)
{
var myDataSource = await _myDataSourceTask;
// Simple linear search is acceptable for small data sets
var matches = myDataSource.AllGroups.Where(group => group.UniqueId == uniqueId);
if (matches.Count() == 1) return matches.First();
return null;
}
If LoadData is async, then store whatever the awaitable is (or make a new one) and expose that (for instance, like a Task) then GetGroup can be marked async and can do var data = await _myDataSource.LoadTask or whatever
I think Svick is the closest to answering the question because of his use of Tasks. Whether you return a Task on the GetGroup method or you return a task on a LoadAsync method probably doesn't matter. What DOES matter is that you capture that task and refer to it later on resume.
The Task class is documented here and you'll notice it has properties like IsCanceled, IsCompleted and IsFaulted. When your constructor kicks off the LoadAsync method you could save the Task it returns as a class-level variable. Later, when your resume code starts, you can check to see whether the Task is still running (i.e. not completed and not faulted). If the Task has completed, you can run your resume code right away. If it hasn't completed yet, you can use Task.ContinueWith to schedule your resume code to be run when the task completes.
I did not check out Windows 8 yet, but I assume it works similar to Windows Phone, assuming you use Silverlight (or WPF) to develop your app, this is not clear to me from your question. In case you use Silverlight:
You need to use the INotifyPropertyChanged interface, and ObservableCollection-s to be able to bind your data to the UI. That way everytime you change your data, the UI will be notified about the changes, and bindings will refresh.
MyDataGroup a pubic property that implements iNotifyPropertyChanged.
UniqueID a public property.
When UniqueID changes set MyDataGroup = null then call a BackGroundWorker to var mathces = but delay that if LoadData(); is working. Since this is in the background the delay will not tie up the UI. In the callback set MyDataGroup and the UI will get notified. Make sure you backgroundworker is cancelable as you will want to cancel it when UniqueID is changed (if it is running).
I do this in WPF so if it does not translate to Metro sorry and I will delete.
Related
I'm trying to refactor an existing synchronous app into one that uses Async/Await. This WPF app was written originally using a BackgroundWorker thread calling a bunch of synchronous methods. I went down to the deepest level and worked my way up, converting these methods to async, using await Task.Run(()..) on some of them that didn't seem to be taking too long.
I am stuck on something though. I want to pass in an IProgress parameter, to be able to get feedback into the UI -- and the last, deepest method, the one that takes the longest to execute, is written this way:
public static Task<bool> SaveToFileAsync(string filePath)
{
return Task.Run(() => SaveToFile(filePath));
/*
SaveToFile calls into an unmanaged dll that uses a few loops and writes
to file using fopen/fwrite...
*/
}
private async void CreateObjectAndSaveToFile(/*IProgress progress*/)
{
List<Task> SaveFileTaskArray = new List<Task>();
int index = 0;
foreach (...)
{
//
{
DoSomethingThatTakesSomeTime();
//Start Async Task to save this SomeThing to file
SaveFileTaskArray.Add(SaveToFileAsync(fileName));
}
index++;
}
await Task.WhenAll(SaveFileTaskArray);
}
Now if I call CreateObjectAndSaveToFile() on the UI side like so:
await CreateObjectAndSaveToFile();
I get the desired effect - UI is responsive and files get saved one by one (can be 10-20 files). But for completeness, I'd like to add a progress bar to this (and therefore adding that IProgress parameter all the way down).
I'm not sure how I can do that.
You mention in the comments that you know how to use IProgress already. So if you want report progress as each task finishes, you can use WhenAny() instead of WhenAll(), and remove each task from the list as it finishes. Something like this:
while (SaveFileTaskArray.Count > 0) {
var finishedTask = await Task.WhenAny(SaveFileTaskArray);
SaveFileTaskArray.Remove(finishedTask);
//Report progress of how many are left
progress.Report(SaveFileTaskArray.Count);
}
The Microsoft documentation actually has a whole article about this, if you want to read more: Start Multiple Async Tasks and Process Them As They Complete
I am trying to make a function that when called returns back information to the caller that is on a server. What I want in this function, is that it creates a thread that issues the command to the server, and then suspends itself until the server responds back with the answer.
public AccountState GetAccount(string key)
{
AccountState state = null;
Thread t = new Thread(() =>
{
_connection.SomeCommandSentToServer(key);
accountRequests.TryAdd(key, (Thread.CurrentThread, null));
//Suspend current thread until ServerReponseHere is called
Thread.CurrentThread.Suspend();
//We have been resumed, value should be in accountRequests now
accountRequests.TryRemove(key, out var item);
state = item.AccountState;
});
t.Start();
return state;
}
public ConcurrentDictionary<string, (Thread Thread, AccountState AccountState)> accountRequests = new ConcurrentDictionary<string, (Thread Thread, AccountState AccountState)>();
///Once server is done with processed command, call to this function made
public void ServerReponseHere(string key, AccountState state)
{
accountRequests.TryGetValue(username, out var item);
accountRequests.TryUpdate(username, (item.Thread, new AccountState()), item);
item.Thread.Resume();
}
My Idea then is that in a different function, when server responds back, it calls the ResumeThread function shown above.
C# says that Suspend / Resume are deprecated functions however, -- what is a better way to do this?
UPDATE
Clarification about "SomeCommandSentToServer" -- This just sends a command to the server via TCP sockets.
In that call, all that is really happening is a transmission to the server. I'm using a library that uses WinSock2.h call of "Send()" -- Yes I know it is a deprecated library... but the library I'm using requires it.
I have a separate thread that polls input from the server. So I have no way to "await" on this SomeCommandSentToServer -- I would need to await on some sort of call back function (aka the resume function I was mentioning) -- to make this work.
I am unsure how to do that
With all the information available from the question, here is what you should aim for when using the async / await pattern:
public async Task<AccountState> GetAccountAsync(string key)
{
// The method SomeCommandSentToServerAsync must be changed to support async.
AccountState state = await _connection.SomeCommandSentToServerAsync(key);
return state;
}
It is highly unlikely that you need anything else. By that, I mean you will not have to manipulate threads directly, put them in a concurrent dictionary and manually suspend or resume them because it looks horrible from a maintenance perspective ;)
.NET will take care of the threading part, meaning the magic of the async infrastructure will most likely release the current thread (assuming a call is actually made to the server) until the server returns a response.
Then the infrastructure will either use the existing synchronization context -if you are on a UI thread for instance- or grab a thread from the thread pool -if not- to run the rest of the method.
You could even reduce the size of the method a bit more by simply returning a Task with a result of type AccountState:
public Task<AccountState> GetAccountAsync(string key)
{
// The method SomeCommandSentToServerAsync must be changed to support async.
return _connection.SomeCommandSentToServerAsync(key);
}
In both example, you will haver to make the callers async as well:
public async Task TheCallerAsync()
{
// Grab the key from somewhere.
string key = ...;
var accountState = await <inst>.GetAccountAsync(key);
// Do something with the state.
...
}
Turning a legacy method into an async method
Now, regarding the legacy SomeCommandSentToServer method. There is a way to await that legacy method. Yes, you can turn that method into an asynchronous method that can be used with the async / await.
Of course, I do not have all the details of your implementation but I hope you will get the idea of what needs to be done. The magical class to do that is called TaskCompletionSource.
What it allows you to do is to give you access to a Task. You create the instance of that TaskCompletionSource class, you keep it somewhere, you send the command and immediately return the Task property of that new instance.
Once you get the result from your polling thread, you grab the instance of TaskCompletionSource, get the AccountState and call SetResult with the account state. This will mark the task as completed and do the resume part you were asking for :)
Here is the idea:
public Task<AccountState> SomeCommandSentToServerAsync(string key)
{
var taskCompletionSource = new TaskCompletionSource<AccountState>();
// Find a way to keep the task in some state somewhere
// so that you can get it the polling thread.
// Do the legacy WinSock Send() command.
return taskCompletionSource.Task;
}
// This would be, I guess, your polling thread.
// Again, I am sure it is not 100% accurate but
// it will hopefully give you an idea of where the key pieces must be.
private void PollingThread()
{
while(must_still_poll)
{
// Waits for some data to be available.
// Grabs the data.
if(this_is_THE_response)
{
// Get the response and built the account state somehow...
AccountState accountState = ...
// Key piece #1
// Grab the TaskCompletionSource instance somewhere.
// Key piece #2
// This is the magic line:
taskCompletionSource.SetResult(accountState);
// You can also do the following if something goes wrong:
// taskCompletionSource.SetException(new Exception());
}
}
}
I have a Winform project that inside of the winform class I have a property called DataBindingTasks like so.
// create a task list to determine when tasks have finished during load
protected List<Task> DataBindingTasks = new List<Task>();
I have several async void methods that I am calling in the winform "Load" event that are all similar to the following.
private async void BindSomething(int millSecToWait)
{
var someTask = Task.Factory.StartNew(() =>
{
// do some work
System.Threading.Thread.Sleep(millSecToWait);
// return some list for binding
return new List<int>();
});
// add the task to the task list
DataBindingTasks.Add(someTask);
// wait until data has loaded
var listToBind = await someTask;
// bind the data to a grid
}
I am calling the BindSomething methods on load.
I say methods because there are several of these binding types of methods that are called on load.
private void Form_Load(object sender, EventArgs e)
{
// async bind something and let UI continue
// fire and forget
BindSomething(5000);
BindSomething(8000);
BindSomething(2000);
BindSomething(2000);
// code to execute when all data binding tasks have completed
Task.WhenAll(DataBindingTasks).ContinueWith((x) =>
{
// Do something after all async binding tasks have completed
});
}
EXCEPT the ContinueWith code is executing even though all the tasks have not completed.
Here is a screen shot showing that all task are not complete.
UPDATED 10/29
The problem is obviously deeper than the sample code above and the sample code above does not fully explain the true scenario.
I will try to explain in greater detail but try to not make it to long.
This is a Winform application.
We have created a base winform "BaseForm" that all other winforms will inherit from.
We have overridden the "OnLoad" event in the "BaseForm" so that we can call a new method that all inherited forms will have called "LoadData".
Since "LoadData" can have async method calls, the base form needs to know when the "LoadData" method is finished.
So in the base form was have some of the following:
protected List<Task> DataBindingTasks = new List<Task>();
public event EventHandler DataBindingTasksComplete;
protected void OnDataBindingTasksComplete(EventArgs e)
{
if (DataBindingTasksComplete != null)
{
DataBindingTasksComplete(this, e);
}
// now clear the list
DataBindingTasks.Clear();
}
// NOTE: this is inside the OnLoad called before base.OnLoad(e)
Task.WhenAll(DataBindingTasks).ContinueWith((x) =>
{
OnDataBindingTasksComplete(EventArgs.Empty);
});
The hope was that all inherited forms would add any of their "async" tasks to this list so that the base form could fire the "DataBindingTasksComplete" event so they would know that form has finished loading.
The problem "as perceived to us at the time of the issue" was that the "WhenAll().ContinueWith" was not waiting until all the tasks on the list had completed.
BUT as someone noted, the list might have changed.
So here is most likely what happened.
There are 4 "BindSomething" methods that are marked async all called from the Form_Load
The 2nd or so line down inside the "BindSomething" method is used to add a task to the "BaseForm.DataBindingTasks" list.
Since each of these calls are marked async, the Form_Load continues to call all 4 as a "fire and forget".
After that, it returns back to the BaseForm OnLoad which then looks at the "DataBindingTasks" list to see if all tasks have completed.
My best guess is that one of the "BindSomething" methods was in the middle of adding its task to the list yet the Base.OnLoad has already started looking at the list.
I could add 4 "fake" tasks (like thread sleep) to the list even before calling the "BindSomething" methods as "place holders" and then inside the "BindSomething" methods swap out the "fake" tasks with the "real" tasks.
This seams messy and most likely will cause other issues.
The most likely fix is to not use a task list / WhenAll.ContinueWith and instead call the load data with "await" and then raise the event on the next line.
The async void methods are called as fire-and-forget, and there is no way to wait for them, that's why your delegate don't wait properly - it simply can't do that. So you need some changes in your code.
Update: #Servy noted the main problem in your code which I've missed, thanks for him:
DataBindingTasks.Add(someTask);
This operation isn't thread-safe! You simply losing some of your tasks during parallel calls for Add method. You need to change this: by using lock, by using ConcurrentCollection or by using data separation: assign a task to array by different indexes so parallel tasks aren't intersect each other.
First of all, you shouldn't use the StartNew in this case, use the Task.Run, otherwise you can met some problems in your app.
Second thing is that you can make the Load method async and await it, so your UI wouldn't freeze, and you can switch the signature for your BindSomething methods to became awaitable, as #digimunk mentioned:
// note that we return the task here
private async Task BindSomething(int millSecToWait)
{
// use Task.Run in this case
var someTask = Task.Run(() =>
{
// Some work
System.Threading.Thread.Sleep(millSecToWait);
// return some list for binding
return new List<int>();
});
DataBindingTasks.Add(someTask);
// wait until data has loaded
var listToBind = await someTask;
// bind the data to a grid
}
// async void for the event handler
private async void Load()
{
// start tasks in fire-and-forget fashion
BindSomething(5000);
BindSomething(8000);
BindSomething(2000);
// code to execute when all data binding tasks have completed
await Task.WhenAll(DataBindingTasks);
// Do something after all binding is complete
}
In this case you can await the Load method safely.
You don't need .ContinueWith(). Just await the Task.WhenAll(), and then put whatever code you want to run after it under it. Also, change the "void" in the method signature to "async Task".
I have a synchronous method I am calling with Task.Run() and my UI is being blocked and unresponsive. The method loads information from a database via COM Interop and I don't have any control over that.
public List<EdmAddInInfo2> GetInstalledAddins()
{
IEdmAddInMgr7 addinMgr = m_vault as IEdmAddInMgr7;
Array installedAddins = Array.CreateInstance(typeof(EdmAddInInfo2), 0);
addinMgr.GetInstalledAddIns(out installedAddins);
if (installedAddins?.Length > 0)
return installedAddins.OfType<EdmAddInInfo2>().ToList();
return null;
}
I am calling the method this way when my form is shown;
private async void LicensesForm_Shown(object sender, EventArgs e)
{
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
toolStripStatusLabel2.Text = $"Loaded {m_addins.Count} addins.";
}
private async Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
AddinManager addinMgr = new AddinManager(Vault);
var addins = await Task.Run(() => addinMgr.GetInstalledAddins()).ConfigureAwait(false);
return addins;
}
Usually I would use a BCW and be on my way, but I figured I would give Tasks a shot. Any ideas?
After the discussion in comments, I'm thinking that there is something deeper going on involving COM. One thing to be aware of when involving COM is that it uses the Dispatcher for receiving events, which has bitten me before. If the issue is related to COM one would probably need more information about what is going on and that digging might not be worthwhile. I wish I could be more helpful, but I think I'll have to default to advising the easy way out. Launch a thread to call GetInstalledAddins, assign the result to a local variable, and notify the UI of completion through the Dispatcher.
Also, from my original answer before editing to add the above,
var m_addins = await GetInstalledAddins().ConfigureAwait(false);
should be:
var m_addins = await GetInstalledAddins().ConfigureAwait(true);
This is because on the next line you assign to a UI element's Text property. This assignment must be done from the UI thread, which is active when you call GetInstalledAddins(), but because you then call ConfigureAwait(false) the execution continues after await on any thread the async manager (I forget what it's called) chooses.
One of the strengths of using async/await in UI code is that execution can (in normal situations) be resumed on the same thread the await call was made on. This way you can continue accessing UI objects after the await. But your call to ConfigureAwait(false) instructs the async/await engine that you don't care which thread the execution is resumed on (but in this case you really should care that the execution is resumed on the same thread).
The method loads information from a database via COM Interop and I don't have any control over that.
Well then, depending on the implementation of that method, it may not be possible to unblock the UI thread.
However, you could try this: if that type is allocating COM objects in its constructor, they may be getting tied to the UI thread. I would try creating the instance on the background thread:
private Task<List<EdmAddInInfo2>> GetInstalledAddins()
{
return Task.Run(() => new AddinManager(Vault).GetInstalledAddins());
}
Usually I would use a BCW and be on my way
BackgroundWorker would have the exact same problem.
Background Workflow:
I have a client (jquery/ajax html page) calling our RESTful WebAPI to get some data (Patient 'encounters' - e.g. admission to a hospital, visit to a clinic, etc.). e.g.
public async Task<string> GetEncounters(string patientId)
{
PatientChart patientChart = await _myService.GetPatientChart(patientId);
string message = string.Empty;
if (patientChart.Encounters.Status == PatientChart.StatusNotApplicable)
{
message = "List of Encounters is not available. A request has been made to retrieve it.";
_myService.GetEncounters(patientId); // async method without call to await
}
return message;
}
Question
What happens the "GetEncounters" call above where the await keyword is not applied?
From my understanding, async methods do NOT generate a new thread
so when the main thread dies, does that mean the call to GetEncounters will abort?
(Behind the scenes, the GetEncounters will fire off a long running process to get data and store it in, e.g. cache, for later retrieval).
If I step through the code, every executes as expected. Likewise, if I add in the await keyword, it also works. (But then I make the caller block)
What's the solution?
i.e. what is the best way to create a background task/thread to execute the code even though the main thread has died?
The solution depends on how reliable you want the work to be. I.e., if you return the "not available" message, how important is it to you that GetEncounters will run?
You should at the very least register the background work with ASP.NET (i.e., HostingEnvironment.QueueBackgroundWorkItem). A more reliable solution would save the background work in storage and have an independent backend for processing. I describe some modern approaches on my blog.