patterncsharpMinor
Use and understanding of async/await in .NET 4.5 +
Viewed 0 times
understandingasyncawaitnetanduse
Problem
I am just about to embark upon a massive job of multi-threading a cost-engine. I could use TPL of which I am very familiar, but would like to leverage the
Is my understanding of what is going on correct? How can I improve this design?
```
CancellationTokenSource cancelSource;
// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
IProgress progressInfo = new Progress(ReportProgress);
await ProcessScript(progressInfo, token, uiScheduler);
// await sets up the following as an Contnuation to
// continue once returned from await.
this.resultsTextBox.AppendText("\nReturn to caller...\n\n");
}
// Class to report progress...
private void ReportProgress(ProgressInfo progressInfo)
{
this.progressBar.Value = progressInfo.PrecentComplete;
this.progressLabel.Content = progressInfo.Message;
}
private async Task ProcessScript(IProgress progressInfo,
CancellationToken token,
TaskScheduler uiScheduler)
{
ProgressInfo pi = new ProgressInfo();
pi.PrecentComplete = 0;
pi.Message = "In script processor...";
progressInfo.Report(pi);
Thread.Sleep(5000); // This is UI Blocking...
string str = this.resultsTextBox.Text;
str = "We have added this => going into await...";
this.resultsTextBox.AppendText(str);
Task task = Task.Factory.StartNew(() => LongRunning(uiScheduler));
pi.Message = "Task set";
pi.PrecentComplete = 50;
progressInfo.Report(pi);
// awaits the long runniing task - non-UI blocking.
await task;
// The await above sets this up as a continuation on the UI thread.
pi.Message =
async/await keywords of .NET 4.5 to make life that little bit simpler.Is my understanding of what is going on correct? How can I improve this design?
```
CancellationTokenSource cancelSource;
// Mark the event handler with async so you can use await in it.
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
IProgress progressInfo = new Progress(ReportProgress);
await ProcessScript(progressInfo, token, uiScheduler);
// await sets up the following as an Contnuation to
// continue once returned from await.
this.resultsTextBox.AppendText("\nReturn to caller...\n\n");
}
// Class to report progress...
private void ReportProgress(ProgressInfo progressInfo)
{
this.progressBar.Value = progressInfo.PrecentComplete;
this.progressLabel.Content = progressInfo.Message;
}
private async Task ProcessScript(IProgress progressInfo,
CancellationToken token,
TaskScheduler uiScheduler)
{
ProgressInfo pi = new ProgressInfo();
pi.PrecentComplete = 0;
pi.Message = "In script processor...";
progressInfo.Report(pi);
Thread.Sleep(5000); // This is UI Blocking...
string str = this.resultsTextBox.Text;
str = "We have added this => going into await...";
this.resultsTextBox.AppendText(str);
Task task = Task.Factory.StartNew(() => LongRunning(uiScheduler));
pi.Message = "Task set";
pi.PrecentComplete = 50;
progressInfo.Report(pi);
// awaits the long runniing task - non-UI blocking.
await task;
// The await above sets this up as a continuation on the UI thread.
pi.Message =
Solution
Yes, your understanding is correct. Note that you're not using
Following your comments:
This is correct since
Here we release the UI thread and running a (computing) task in parallel. The reason I've added "computing" is that you're manually spanning a new task using
Correct. See good article Await, SynchronizationContext, and Console Apps that describes in details the behavior of
You're passing
CancellationToken provided to ProcessScript. If your operations are cancellable and/or you're using other asynchronous API that accepts CancellationTokens it's highly recommended to pass it through the code, otherwise just don't create the CancellationTokenSource.Following your comments:
Thread.Sleep(5000); // This is UI Blocking...This is correct since
async methods return control to the caller only when something is awaited.// awaits the long runniing task - non-UI blocking.
await task;Here we release the UI thread and running a (computing) task in parallel. The reason I've added "computing" is that you're manually spanning a new task using
Task.Factory. In case of I/O-related task returned by .NET framework it won't actually represent a new thread as it can wait for external data on the same thread.// The await above sets this up as a continuation on the UI thread.Correct. See good article Await, SynchronizationContext, and Console Apps that describes in details the behavior of
await.// Allow access to the UI from this background thread from
// the ThreadPool.
Task.Factory.StartNew(() =>
{
this.resultsTextBox.AppendText("\n\nNow in 'LongRunning'!! Waiting 5s simulating HARD WORK!!");
}, CancellationToken.None,
TaskCreationOptions.None,
uiScheduler);You're passing
TaskScheduler corresponding to UI thread so naturally the code will be safe to work with UI. I would recommend using IProgress to update/inform UI about changes in long-running task though, since you may want to separate computation logic from UI-related code.Code Snippets
Thread.Sleep(5000); // This is UI Blocking...// awaits the long runniing task - non-UI blocking.
await task;// The await above sets this up as a continuation on the UI thread.// Allow access to the UI from this background thread from
// the ThreadPool.
Task.Factory.StartNew(() =>
{
this.resultsTextBox.AppendText("\n\nNow in 'LongRunning'!! Waiting 5s simulating HARD WORK!!");
}, CancellationToken.None,
TaskCreationOptions.None,
uiScheduler);Context
StackExchange Code Review Q#20820, answer score: 4
Revisions (0)
No revisions yet.