Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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 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> progressInfo = new Progress<ProgressInfo>(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> 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 = "Task awaited";
    pi.PrecentComplete = 95;
    progressInfo.Report(pi);

    this.resultsTextBox.AppendText("Where are we now??");
    return;
}

private bool LongRunning(TaskScheduler uiScheduler)
{
    // 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);

    // Simulate hard work.
    Thread.Sleep(7000);
    Task.Factory.StartNew(() =>
    {
        this.resultsTextBox.AppendText("\n\n\nHARD WORK COMPLETE!!");
    }, CancellationToken.None,
       TaskCreationOptions.None,
       uiScheduler);
    return true;
}
share|improve this question
    
You may want to rephrase the question to better fit the code-review style described in the faq. There are currently a couple of close requests because questions like "Help me understand this code" are off topic here. "How can I improve this async/await code", for instance, would be a better fit. –  codesparkle Jan 23 '13 at 14:04
    
I'm confused. The sight is called "CodeReview", I am asking someone to do exactly that, review my code. I will take you advice and make an edit; I hope it helps and thanks for your time... –  Killercam Jan 23 '13 at 15:50
    
What's off topic is to ask us to explain the code to you. As long as you aren't asking that, its okay. –  Winston Ewert Jan 23 '13 at 20:28

1 Answer 1

up vote 3 down vote accepted

Yes, your understanding is correct. Note that you're not using 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<T> to update/inform UI about changes in long-running task though, since you may want to separate computation logic from UI-related code.

share|improve this answer
    
Thanks very much for your time. –  Killercam Jan 23 '13 at 13:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.