Reputation: 5133
Here is what i have tried - It works, as far as seeing the UI refreshed, but i don't think it's the best use of async / await. How can i improve on this?
private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
XmlReader reader;
int rowCount = 0;
using (reader = XmlReader.Create("someXmlFile.xml"))
{
while (reader.Read())
{
rowCount++;
DoSomeProcessingOnTheUIThread(reader);
//only update UI every 100 loops
if (rowCount > 0 && rowCount % 100 == 0)
{
//yay! release the UI thread
await Task.Run(() =>
{
Application.Current.Dispatcher.Invoke(
() =>
{
txtRowCount.Text = string.Format("{0}", rowCount);
});
});
}
} //end-while
}//end-using
}
What's a better approach?
UPDATE: I have avoided the Dispatcher.Invoke based on the answer of Clemens, by sending the processing to a background task, and updating progress on the UI directly. My code now looks like.
private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
XmlReader reader;
int rowCount = 0;
using (reader = XmlReader.Create("someXmlFile.xml"))
{
while (reader.Read())
{
rowCount++;
await DoSomeProcessing(reader);
//only update UI every 100 loops
if (rowCount % 100 == 0)
{
txtRowCount.Text = string.Format("{0}", rowCount);
}
} //end-while
}//end-using
MessageBox.Show("I am done!");
}
private Task DoSomeProcessing(XmlReader reader)
{
Task t =Task.Run(() =>
{
//Do my processing here.
});
return t;
}
UPDATE#2: On reflection, why am i creating a new Task on every loop ? It would probably be better to just run the whole loop within one background task. And periodically raise a callback to show progress; See my other answer below.
Upvotes: 4
Views: 8246
Reputation: 5133
My Final answer.
private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => DoSomeWorkOnBgThread( (cnt) => //HAHA! this syntax is so confusing
{
Application.Current.Dispatcher.Invoke(() =>
{
txtRowCount.Text = cnt.ToString();
}
); //end-invoke
}
));
MessageBox.Show("i am done!");
}
private void DoSomeWorkOnBgThread(Action<int> callbackFn)
{
XmlReader reader;
int rowCount = 0;
using (reader = XmlReader.Create("someXmlFile.xml"))
{
while (reader.Read())
{
rowCount++;
DoMyProcessingHere();
//only update UI every 100 loops
if (rowCount % 100 == 0)
{
callbackFn(rowCount); ///
}
} //end-while
}//end-using
}
Upvotes: 0
Reputation: 128062
A Task that immediately calls Dispatcher.Invoke
is pointless, because there is nothing that actually runs on a background thread, except the tiny piece of code that schedules the Dispatcher action.
Better set the Text property directly, and use XmlReader.ReadAsync
:
private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
using (var reader = XmlReader.Create("someXmlFile.xml"))
{
int rowCount = 0;
while (await reader.ReadAsync())
{
rowCount++;
await Task.Run(() =>
{
DoSomeWork(reader);
});
if (rowCount > 0 && rowCount % 100 == 0)
{
txtRowCount.Text = string.Format("{0}", rowCount);
}
}
}
}
You may also consider making your DoSomeWork
async and directly call it like this:
await DoSomeWork(reader);
Upvotes: 5