views:

343

answers:

5

Microsoft just announced the new C# Async feature. Every example I've seen so far is about asynchronously downloading something from HTTP. Surely there are other important async things?

Suppose I'm not writing a new RSS client or Twitter app. What's interesting about C# Async for me?

Edit I had an Aha! moment while watching Anders' PDC session. In the past I have worked on programs that used "watcher" threads. These threads sit waiting for something to happen, like watching for a file to change. They aren't doing work, they're just idle, and notify the main thread when something happens. These threads could be replaced with await/async code in the new model.

+17  A: 

Ooh, this sounds interesting. I'm not playing with the CTP just yet, just reviewing the whitepaper. After seeing Anders Hejlsberg's talk about it, I think I can see how it could prove useful.

As I understand, async makes writing asynchronous calls easier to read and implement. Very much in the same way writing iterators is easier right now (as opposed to writing out the functionality by hand). This is essential blocking processes since no useful work can be done, until it is unblocked. If you were downloading a file, you cannot do anything useful until you get that file letting the thread go to waste. Consider how one would call a function which you know will block for an undetermined length and returns some result, then process it (e.g., store the results in a file). How would you write that? Here's a simple example:

static object DoSomeBlockingOperation(object args)
{
    // block for 5 minutes
    Thread.Sleep(5 * 60 * 1000);

    return args;
}

static void ProcessTheResult(object result)
{
    Console.WriteLine(result);
}

static void CalculateAndProcess(object args)
{
    // let's calculate! (synchronously)
    object result = DoSomeBlockingOperation(args);

    // let's process!
    ProcessTheResult(result);
}

Ok good, we have it implemented. But wait, the calculation takes minutes to complete. What if we wanted to have an interactive application and do other things while the calculation took place (such as rendering the UI)? This is no good, since we called the function synchronously and we have to wait for it to finish effectively freezing the application since the thread is waiting to be unblocked.

Answer, call the function expensive function asynchronously. That way we're not bound to waiting for the blocking operation to complete. But how do we do that? We'd call the function asynchronously and register a callback function to be called when unblocked so we may process the result.

static void CalculateAndProcessAsyncOld(object args)
{
    // obtain a delegate to call asynchronously
    Func<object, object> calculate = DoSomeBlockingOperation;

    // define the callback when the call completes so we can process afterwards
    AsyncCallback cb = ar =>
        {
            Func<object, object> calc = (Func<object, object>)ar.AsyncState;
            object result = calc.EndInvoke(ar);

            // let's process!
            ProcessTheResult(result);
        };

    // let's calculate! (asynchronously)
    calculate.BeginInvoke(args, cb, calculate);
}
  • Note: Sure we could start another thread to do this but that would mean we're spawning a thread that just sits there waiting to be unblocked, then do some useful work. That would be a waste.

Now the call is asynchronous and we don't have to worry about waiting for the calculation to finish and process, it's done asynchronously. It will finish when it can. An alternative to calling code asynchronously directly, you could use a Task:

static void CalculateAndProcessAsyncTask(object args)
{
    // create a task
    Task<object> task = new Task<object>(DoSomeBlockingOperation, args);

    // define the callback when the call completes so we can process afterwards
    task.ContinueWith(t =>
        {
            // let's process!
            ProcessTheResult(t.Result);
        });

    // let's calculate! (asynchronously)
    task.Start();
}

Now we called our function asynchronously. But what did it take to get it that way? First of all, we needed the delegate/task to be able to call it asynchronously, we needed a callback function to be able to process the results, then call the function. We've turned a two line function call to much more just to call something asynchronously. Not only that, the logic in the code has gotten more complex then it was or could be. Although using a task helped simplify the process, we still needed to do stuff to make it happen. We just want to run asynchronously then process the result. Why can't we just do that? Well now we can:

// need to have an asynchronous version
static async Task<object> DoSomeBlockingOperationAsync(object args)
{
    //it is my understanding that async will take this method and convert it to a task automatically
    return DoSomeBlockingOperation(args);
}

static async void CalculateAndProcessAsyncNew(object args)
{
    // let's calculate! (asynchronously)
    object result = await DoSomeBlockingOperationAsync(args);

    // let's process!
    ProcessTheResult(result);
}

Now this was a very simplified example with simple operations (calculate, process). Imagine if each operation couldn't conveniently be put into a separate function but instead have hundreds of lines of code. That's a lot of added complexity just to gain the benefit of asynchronous calling.


Another practical example used in the whitepaper is using it on UI apps. Modified to use the above example:

private async void doCalculation_Click(object sender, RoutedEventArgs e) {
    doCalculation.IsEnabled = false;
    await DoSomeBlockingOperationAsync(GetArgs());
    doCalculation.IsEnabled = true;
}

If you've done any UI programming (be it WinForms or WPF) and attempted to call an expensive function within a handler, you'll know this is handy. Using a background worker for this wouldn't be that much helpful since the background thread will be sitting there waiting until it can work.


Suppose you had a way to control some external device, let's say a printer. And you wanted to restart the device after a failure. Naturally it will take some time for the printer to start up and be ready for operation. You might have to account for the restart not helping and attempt to restart again. You have no choice but to wait for it. Not if you did it asynchronously.

static async void RestartPrinter()
{
    Printer printer = GetPrinter();
    do
    {
        printer.Restart();

        printer = await printer.WaitUntilReadyAsync();

    } while (printer.HasFailed);
}

Imagine writing the loop without async.


One last example I have. Imagine if you had to do multiple blocking operations in a function and wanted to call asynchronously. What would you prefer?

static void DoOperationsAsyncOld()
{
    Task op1 = new Task(DoOperation1Async);
    op1.ContinueWith(t1 =>
    {
        Task op2 = new Task(DoOperation2Async);
        op2.ContinueWith(t2 =>
        {
            Task op3 = new Task(DoOperation3Async);
            op3.ContinueWith(t3 =>
            {
                DoQuickOperation();
            }
            op3.Start();
        }
        op2.Start();
    }
    op1.Start();
}

static async void DoOperationsAsyncNew()
{
    await DoOperation1Async();

    await DoOperation2Async();

    await DoOperation3Async();

    DoQuickOperation();
}

Read the whitepaper, it actually has a lot of practical examples like writing parallel tasks and others.

I can't wait to start playing with this either in the CTP or when .NET 5.0 finally makes it out.

Jeff M
I'm not sure the syntax is 100% sound using async but you get the idea.
Jeff M
In the case of a UI, I think a `BackgroundWorker` would be a better choice then using `async` and `await`.
Brian
Wrong. `async` does NOT start any threads, so your DoSomeVeryExpensiveCalculation() will NOT be executed in the background. Remember that `async` is for operations that run only for a short time and then `await` something. If you want to run long CPU-intensive operation in the background, you need to explicitly do that in the background:static Task<object> DoSomeVeryExpensiveCalculationAsync(object args){ return Task.CreateNew(() => DoSomeVeryExpensiveCalculation(args));}
Daniel
The new language support is nice because it allows you to easily *compose* asynchronous operations. It's for composition, not for auto-parallelizing. (your doCalculation_Click is a good example of composition)
Daniel
@Daniel: I've updated the answer. I have to admit, I don't use asynchronous calls often nowadays except when writing networking code. Hopefully it should be satisfactory now.
Jeff M
+5  A: 

I've found another nice use-case for this today: you can await user interaction.

For example, if one form has a button that opens another form:

Form toolWindow;
async void button_Click(object sender, EventArgs e) {
  if (toolWindow != null) {
     toolWindow.Focus();
  } else {
     toolWindow = new Form();
     toolWindow.Show();
     await toolWindow.OnClosed();
     toolWindow = null;
  }
}

Granted, this isn't really any simpler than

toolWindow.Closed += delegate { toolWindow = null; }

But I think it nicely demonstrates what await can do. And once the code in the event handler is non-trivial, await make programming much easier. Think about the user having to click a sequence of buttons:

async void ButtonSeries()
{
  for (int i = 0; i < 10; i++) {
    Button b = new Button();
    b.Text = i.ToString();
    this.Controls.Add(b);
    await b.OnClick();
    this.Controls.Remove(b);
  }
}

Sure, you could do this with normal event handlers, but it would require you to take apart the loop and convert it into something much harder to understand.

Remember that await can be used with anything that gets completed at some point in the future. Here's the extension method Button.OnClick() to make the above work:

public static AwaitableEvent OnClick(this Button button)
{
    return new AwaitableEvent(h => button.Click += h, h => button.Click -= h);
}
sealed class AwaitableEvent
{
    Action<EventHandler> register, deregister;
    public AwaitableEvent(Action<EventHandler> register, Action<EventHandler> deregister)
    {
        this.register = register;
        this.deregister = deregister;
    }
    public EventAwaiter GetAwaiter()
    {
        return new EventAwaiter(this);
    }
}
sealed class EventAwaiter
{
    AwaitableEvent e;
    public EventAwaiter(AwaitableEvent e) { this.e = e; }

    Action callback;

    public bool BeginAwait(Action callback)
    {
        this.callback = callback;
        e.register(Handler);
        return true;
    }
    public void Handler(object sender, EventArgs e)
    {
        callback();
    }
    public void EndAwait()
    {
        e.deregister(Handler);
    }
}

Unfortunately it doesn't seem possible to add the GetAwaiter() method directly to EventHandler (allowing await button.Click;) because then the method wouldn't know how to register/deregister that event. It's a bit of boilerplate, but the AwaitableEvent class can be re-used for all events (not just UI). And with a minor modification and adding some generics, you could allow retrieving the EventArgs:

MouseEventArgs e = await button.OnMouseDown();

I could see this being useful with some more complex UI gestures (drag'n'drop, mouse gestures, ...) - though you'd have to add support for cancelling the current gesture.

Daniel
+3  A: 

There are some samples and demos in the CTP that don't use the Net, and even some that don't do any I/O.

And it does apply to all multithreaded / parallel problem areas (that already exist).

Async and Await are a new (easier) way of structuring all parallel code, be it CPU-bound or I/O bound. The biggest improvement is in areas where before C#5 you had to use the APM (IAsyncResult) model, or the event model (BackgroundWorker, WebClient). I think that is why those examples lead the parade now.

Henk Holterman
Agreed... the game of life sample is a simple is worth a look. The main form's load event wires directly to an async method, which awaits on a task launched on another thread.
mancaus
+9  A: 

The main scenarios are any scenario that involves high latency. That is, lots of time between "ask for a result" and "obtain a result". Network requests are the most obvious example of high latency scenarios, followed closely by I/O in general, and then by lengthy computations that are CPU bound on another core.

However, there are potentially other scenarios that this technology will mesh nicely with. For example, consider scripting the logic of a FPS game. Suppose you have a button click event handler. When the player clicks the button you want to play a siren for two seconds to alert the enemies, and then open the door for ten seconds. Wouldn't it be nice to say something like:

button.Disable();
await siren.Activate(); 
await Delay(2000);
await siren.Deactivate();
await door.Open();
await Delay(10000);
await door.Close();
await Delay(1000);
button.Enable();

Each task gets queued up on the UI thread, so nothing blocks, and each one resumes the click handler at the right point after its job is finished.

Eric Lippert
+1  A: 

A GUI clock is a good example; say you want to draw a clock, that updates the time shown every second. Conceptually, you want to write

 while true do
    sleep for 1 second
    display the new time on the clock

and with await (or with F# async) to asynchronously sleep, you can write this code to run on the UI thread in a non-blocking fashion.

http://lorgonblog.wordpress.com/2010/03/27/f-async-on-the-client-side/

Brian