Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Lucian Wischik

Lucian Wischik ljw1004

Niner since 2009

I'm on the VB/C# language design team at Microsoft

  • Tip 2: Distinguish CPU-Bound work from IO-bound work

    @Srini, we also saw this within Visual Studio itself. We wanted to write the new features of the product using async. But they still had to speak to the older synchronous components within VS. The result was that a lot of our async methods had to make blocking calls to those components.

    Result: thread starvation, because we were using all available threads, and the TPL was only giving us one additional thread a second.

     

    Fix: to fix this we've been systematically spreading the "async virus" throughout those components, making them all use async from top to bottom, so that we don't starve the threadpool.

  • Tip 3: Wrap events up in Task-returning APIs and await them

    In the RX sample, where I wrote "accel.ReadingChangedObservable()", it was actually just a call to Observable.FromEventPattern(accel, ...). I omitted the lambdas to keep the code simpler.

    So what you're talking about as an "API" would merely be the call to Observable.FromEventPattern. And what you're saying is that I could have used "Observable.FromEventPattern" identically in both the async version and the RX version. Is that right?

  • Tip 3: Wrap events up in Task-returning APIs and await them

    I'm not sure where "API" is a question? We're already given an event-based API from the accelerometer - I reckon that events will be with us for a long time. The question is (1) whether Shake should be exposed as an event or an IObservable, (2) whether we should implement our logic using RX combinators or language combinators.

  • Tip 3: Wrap events up in Task-returning APIs and await them

    Hi wkempf! You say "IObservable is the correct solution." That might be true, but it's by no means obvious, and requires supporting evidence. That supporting evidence would be in the form of example problems, to see which approach winds up simpler, more maintanable, and more debuggable.

    I'd like to see examples! How about you try writing the apple-game into RX and post here?

    Here's another example. I picked "shake detection" since that's been a common RX example. Let's compare them side by side:

    // Shake detection using async (from a stream of Accelerometer.ReadingChangedEventArgs)
    
    public async void StartShakeWatcher(Accelerometer accel) {
        var t = DateTime.Now;
        while (true) {
            var e = await accel.ReadingChangedAsync();
            if (Math.Pow(e.AccelerationX, 2) + Math.Pow(e.AccelerationY, 2) < 1.1) continue;
            if ((DateTime.Now - t).Milliseconds < 200) Shake(this, null);
            t = DateTime.Now;
        }
    }
    
    
    // Shake detection using RX (from a stream of Accelerometer.ReadingChangedEventArgs)
    
    public static IObservable<Event<AccelerometerArgs>> GetShakeObserver(Accelerometer accel) {
        return accel.ReadingChangedObservable()
               .Where(e => (Math.Pow(e.EventArgs.Reading.AccelerationX,2) +
                            Math.Pow(e.EventArgs.Reading.AccelerationY,2) > 1.1))
               .TimeInterval()
               .Where(k => k.Interval.Milliseconds < 200)
               .Select(k => k.Value);
    }

    And here's how you'd consume the two:

    Private Sub OnShake() Handles ShakeWatcher.Shake
        ' ...
    End Sub
    
    Overrides Sub OnNavigatedTo()
        GetShakeObserver().Subscribe(Sub() ...)
    End Sub
    

    I think this one's a wash. Same number of lines of code. The async one allows single-step debugging and doesn't involve lambdas. The RX one is marginally more efficient.

     

    In cases where you have streams of events and need to apply advanced combinators to them, especially when combing streams in complex ways, then RX is unquestionably the best solution. But where you're just doing orchestration of a UI app like here? then it's not so clear.

     

  • Tip 1: Async void is for top-level event-handlers only

    Hi SonOfSam! Good question. Let's look at a concrete example.

    Sub f(a As Action)
      a()
      Console.WriteLine("done")
    End Sub
    
    f(Async Function()
        Await Task.Delay(100)
        Console.WriteLine("almost-done")
    End Function)


    In VB, when you write a lambda as "Async Function", then it will always be a Task-returning async. When you write it as "Async Sub" then it will always be a void-returning async.

    But VB has a feature called "delegate relaxation". The compiler realizes that it would be type-safe to just automatically drop the return value of a lambda if needed.

    Well, it would be type-safe, but it's not what we wanted. In this case when the compiler drops the returned Task, then (1) the caller "f" can't know when the lambda has finished, and (2) any exceptions caused by the lambda that were stored in the returned Task will be completely lost to everyone.

    So the guidance for VB is this: when you pass an Async Function lambda, you should verify that the method you're calling does indeed accept Func(Of Task) or some other Task-returning delegate. Just like in C#.


    (Incidentally, if you try to pass an Async Sub lambda to a function, where an Async Function lambda would also have worked, you're almost certainly doing the wrong thing. In this case the compiler gives a warning).

  • Tip 3: Wrap events up in Task-returning APIs and await them

    I think this is an important new pattern and want more people to start using it. Here are some more resources:

     

  • Lucian Wischik: What's New in VB11

    @Doctor Who:

    ByVal is implicit now: if you omit ByVal/ByRef, then the compiler treats the parameter as ByVal. If you want anything passed by reference, you still need ByRef. Other than that, it's exactly how it always was.

    It's hard to answer your individual questions directly since the answers are more confusing than enlightening... ("What do you mean you're passing my reference by value?!?!!"). Nevertheless: by default everything is passed by value; String objects are immutable references and like all references they are by default passed by value; when you pass a reference to a regular reference-type, the reference is passed by value; if you want to pass a value-type by reference then yes you use ByRef.

     

    @Matt:

    Absolutely. We've already said that we're committed to VB. And you can see we're putting our money where our mouth is -- with the huge slate of new features in VB11, with the exciting developments forthcoming in Roslyn, and with all the new platforms that are supporting VB (including most recently Gadgeteer). My personal opinion is that if a professional .NET developer claims they can't read C# or VB or F#, then I'd hesitate to call them a professional...

  • Lucian Wischik: What's New in VB11

    @Felix: yes the "foreach lifting" is also in C#5. And, it will apply even if you're using VS11/C#5 and downtargeting to older frameworks.

     

    @Felix: in addition to the video of the async talk, there are also the slides and script and source code on my blog: http://blogs.msdn.com/b/lucian/archive/2012/03/29/talk-async-part-1-the-message-loop-and-the-task-type.aspx (The audio quality is terribly for the first few minutes, but gets a little better...)

     

    @Adam: Yes precisely: the await keyword lifted everything else into the continuation...

    In the traditional code I wrote
    op1 = New MessageDialog("hello").ShowAsync()
    op1.Completed = Sub() Dispatcher.InvokeAsync(Sub()
        op2 = New MessageDialog("world").ShowAsync()
    End Sub)
    PlayRestOfGame()

    Note that "PlayRestOfGame" was started immediately after the first dialogbox appeared.

    Meanwhile, in the await code, I wrote
    Await New MessageDialog("hello").ShowAsync()
    Await New MessageDialog("world").ShowAsync()
    PlayRestOfGame()

    This caused the rest of the game to start only after the second dialog had finished.

    Could I have written traditional code to play the game only after the second dialog had finished? Yes, I'd do it with "op2.Completed = Sub() PlayRestOfGame()"

    Could I have written the async code to play the game while the dialogs were still doing there stuff? Yes, I might do something like this:
    Dim background_task = ShowDialogsAsync() ' kick off the dialogs
    PlayRestOfGame()  ' but, without awaiting it, let's just get on with the game

    Async Sub ShowDialogsAsync()
       Await New MessageDialog("hello").ShowAsync()
       Await New MessageDialog("world").ShowAsync()
    End Sub

  • Lucian Wischik: Async Compiler - Bug Fixes, Updates and Core Improvements

    @AdamSpeight2008: I do prefer the C# syntax for generics when writing on whiteboards! And we're often asked for C# syntax for array indices, e.g. here:

    http://blogs.msdn.com/b/lucian/archive/2010/02/13/req8-use-for-arrays.aspx

    But having TWO different syntaxes in VB to write exactly the same thing? I'm sorry but I really don't like that!

     

    --
    Lucian

  • Lucian Wischik: Async Compiler - Bug Fixes, Updates and Core Improvements

    Async integrated into PLINQ?
    Async in linq+plinq+rx feels at first like it would be great. But let's think through some examples...

    string[] urls = new[] {"htp://a.com", "htp://b.com"}
    IAsyncEnumerable<string>[] values =
                  async from url in urls
                  select await new WebClient().DownloadStringTaskAsync(url);

    Note: we're not planning to ship IAsyncEnumerable<T>. But you could easily write it yourself. It would involve the signature "async Task<bool> MoveNextAsync() {...}". Also we're not planning on "async linq". I wrote this example just as a strawman.

     

    Anyway, this example is a bad use of async! Look what it would translate into:

    values = urls.SelectAsync(async (url) => await wc.DownloadStringTaskAsync(url) );

    which is identical to this more efficient version:

    values = urls.Select( (url) => wc.DownloadStringTaskAsync(url) );

     

    The "await" operator unwrapped the Task<string> that came out of DownloadStringTaskAsync into just a string, and then the "async" modifier morally wrapped it right back up into a Task<>.

     

    Let's also be clear that async will always involve some overhead. It's nice to think of LINQ expressions operating over large sets of data, i.e. precisely the places where we don't want this overhead.

     

    There seem to be other places where plinq+rx uses of async would be useful, but without going to the level of integrating async into linq expressions...

See more comments…