Async Dispatcher Calls - Need an explaination
-
So I decided to update an older application with calls using
await...async
instead of embedingThread.Start(() => { myFunction(); });
lambda expressions for long running operations. I have had a few successes but I am a little confused about making asynchronous method call with the dispatcher. Lets start with the original multithreaded database call. It updates aListView
when it is done.public bool PushToDatabase(DataCollection data)
{
try
{
// insert data in database
PerformDatabaseInsert(data);
// then update the visual
App.Current.Dispatcher.Invoke(new Action(() => lst_MyData.ItemsSource = data.ToArray()));
return true;
}
catch (Exception ex)
{
// Display error to user
return false;
}
}This is all wrapped up in a click event in a pop-out control
private void btn_Submit_Click(object sender, RoutedEventArgs e)
{
if (InputsAreGood())
{
DataCollection data = GetFormFields();Thread thread = new Thread(() =>
{
MainWindow.SetCursor_Wait(); // visual candy via the Dispatcherif (PushToDatabase(data))
{
ClosePopOutControl();
}MainWindow.SetCursor_Arrow();
});thread.Start();
}
else
{
// inform user what they missed
}
}Nothing really fancy. The UI remains responsive while the database operation is performed on another thread. Now, I converted these to asynchronous methods. The code works but I am still not understanding why it is supposed to be better when it seems to make the code look a lot more complicated. I can only surmise that I am going about this all wrong. First, I moved the
ListView
update to a separate function.public async Task UpdateListViewAsync(DataCollection data)
{
// Dispatcher is awaitable, so no problems here
await Dispatcher.Invoke(() =>
{
// but you cannot await a void so I have to rewrite it to this
return Task.Run(() => { lst_MyData.ItemsSource = data.ToArray(); });
}
}And added an async function to perform the database call.
public async Task PushToDatabaseAsync(DataCollection data)
{
Task dataPushTask = Task.Run(() => PushToDatabase(data));
bool result = await dataPushTask;
await UpdateListViewAsync();
return result;
}There are fewer lines of code in the function but am I really gaining anything here? What I had to rewrite the submit code to really bakes my noodle.
private btn_Submit
-
So I decided to update an older application with calls using
await...async
instead of embedingThread.Start(() => { myFunction(); });
lambda expressions for long running operations. I have had a few successes but I am a little confused about making asynchronous method call with the dispatcher. Lets start with the original multithreaded database call. It updates aListView
when it is done.public bool PushToDatabase(DataCollection data)
{
try
{
// insert data in database
PerformDatabaseInsert(data);
// then update the visual
App.Current.Dispatcher.Invoke(new Action(() => lst_MyData.ItemsSource = data.ToArray()));
return true;
}
catch (Exception ex)
{
// Display error to user
return false;
}
}This is all wrapped up in a click event in a pop-out control
private void btn_Submit_Click(object sender, RoutedEventArgs e)
{
if (InputsAreGood())
{
DataCollection data = GetFormFields();Thread thread = new Thread(() =>
{
MainWindow.SetCursor_Wait(); // visual candy via the Dispatcherif (PushToDatabase(data))
{
ClosePopOutControl();
}MainWindow.SetCursor_Arrow();
});thread.Start();
}
else
{
// inform user what they missed
}
}Nothing really fancy. The UI remains responsive while the database operation is performed on another thread. Now, I converted these to asynchronous methods. The code works but I am still not understanding why it is supposed to be better when it seems to make the code look a lot more complicated. I can only surmise that I am going about this all wrong. First, I moved the
ListView
update to a separate function.public async Task UpdateListViewAsync(DataCollection data)
{
// Dispatcher is awaitable, so no problems here
await Dispatcher.Invoke(() =>
{
// but you cannot await a void so I have to rewrite it to this
return Task.Run(() => { lst_MyData.ItemsSource = data.ToArray(); });
}
}And added an async function to perform the database call.
public async Task PushToDatabaseAsync(DataCollection data)
{
Task dataPushTask = Task.Run(() => PushToDatabase(data));
bool result = await dataPushTask;
await UpdateListViewAsync();
return result;
}There are fewer lines of code in the function but am I really gaining anything here? What I had to rewrite the submit code to really bakes my noodle.
private btn_Submit
Foothill wrote:
it seems to make the code look a lot more complicated
That's because you've significantly over-complicated it! :)
private btn_Submit_Click(object sender, RoutedEventArgs e)
{
try
{
MainWindow.SetCursor_Wait();
if (InputsAreGood())
{
DataCollection data = GetFormFields():
await PushDataToDatabaseAsync(data);
ClosePopOutForm();
}
else
{
// tell user what fields they missed
}
}
catch (Exception ex)
{
// display the error to the user
}
finally
{
MainWindow.SetCusor_Arrow();
}
}private async Task PushToDatabaseAsync(DataCollection data)
{
// Let any exceptions bubble up the call-stack to be displayed by the calling method.
// TODO: Make this a real async call to the database if possible.
await Task.Run(() => PerformDatabaseInsert(data));// Using "await" means we're back on the UI thread here, // so there should be no need to use the Dispatcher. if (Dispatcher.CheckAccess()) { UpdateList(data) } else { await Dispatcher.BeginInvoke((Action<DataCollection>)UpdateList, data); }
}
private void UpdateList(DataCollection data)
{
lst_MyData.ItemsSource = data.ToArray();
}NB: If at all possible, you should make your
PerformDatabaseInsert
methodasync
, using the built-inasync
methods on theDbConnection
/DbCommand
types. If you're using aDataAdapter
, there's noasync
support, so you're stuck with pushing the update onto a background thread.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
Foothill wrote:
it seems to make the code look a lot more complicated
That's because you've significantly over-complicated it! :)
private btn_Submit_Click(object sender, RoutedEventArgs e)
{
try
{
MainWindow.SetCursor_Wait();
if (InputsAreGood())
{
DataCollection data = GetFormFields():
await PushDataToDatabaseAsync(data);
ClosePopOutForm();
}
else
{
// tell user what fields they missed
}
}
catch (Exception ex)
{
// display the error to the user
}
finally
{
MainWindow.SetCusor_Arrow();
}
}private async Task PushToDatabaseAsync(DataCollection data)
{
// Let any exceptions bubble up the call-stack to be displayed by the calling method.
// TODO: Make this a real async call to the database if possible.
await Task.Run(() => PerformDatabaseInsert(data));// Using "await" means we're back on the UI thread here, // so there should be no need to use the Dispatcher. if (Dispatcher.CheckAccess()) { UpdateList(data) } else { await Dispatcher.BeginInvoke((Action<DataCollection>)UpdateList, data); }
}
private void UpdateList(DataCollection data)
{
lst_MyData.ItemsSource = data.ToArray();
}NB: If at all possible, you should make your
PerformDatabaseInsert
methodasync
, using the built-inasync
methods on theDbConnection
/DbCommand
types. If you're using aDataAdapter
, there's noasync
support, so you're stuck with pushing the update onto a background thread.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
:omg: I can see that. I guess I will chalk that up to this being my first serious attempt and multithreading with the async...await model. Currently reading Best Practices in Asynchronous Programming to get a better idea of what I am doing wrong here and what is the right way. Any other tips to get me started would be appreciated. Does over-complicating something simple make me an engineer? :)
if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
-
:omg: I can see that. I guess I will chalk that up to this being my first serious attempt and multithreading with the async...await model. Currently reading Best Practices in Asynchronous Programming to get a better idea of what I am doing wrong here and what is the right way. Any other tips to get me started would be appreciated. Does over-complicating something simple make me an engineer? :)
if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
Stephen Toub's blog is an excellent resource, although it hasn't been updated in a while, and tends to cover quite advanced topics: Parallel Programming with .NET | All about Async/Await, System.Threading.Tasks, System.Collections.Concurrent, System.Linq, and more…[^] Async/Await FAQ | Parallel Programming with .NET[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer