locked
Can anyone explain this method TaskEx.Yield ? RRS feed

  • Question

  •     //
        // Summary:
        //   Creates an awaitable that asynchronously yields back to the current context
        //   when awaited.
        //
        // Returns:
        //   A context that, when awaited, will asynchronously transition back into the
        //   current context. If SynchronizationContext.Current is non-null, that is
        //   treated as the current context. Otherwise, TaskScheduler.Current is treated
        //   as the current context.
        public static YieldAwaitable Yield();
    
    Can anyone give an example?
    Tuesday, December 7, 2010 10:56 AM

Answers

  • Hi iron9light-

    Conceptually, Yield enables you to temporarily leave the current async method to allow other things to be done on the underlying thread.  Technically, Yield works by posting the rest of the method back to the "current context", where the current context is defined to be the current SynchronizationContext if there is one, or else the current TaskScheduler (which if you're not inside of a task defaults to TaskScheduler.Default).

    The most common usage of Yield would be in places where you may have previously wanted to use something like Application.DoEvents.  Let's say you were writing a UI button click handler:

    public void button1_Click(...)
    {
        foreach(var item in data)
        {
            var result = Process(item);
            listBox1.Items.Add(result);
        }
    }

    If the data list is small, this may be fine to do on the UI thread, but if data has any chance of being large, this foreach loop could end up blocking the UI thread for an unacceptable period of time.  You can thus "Yield" out of it to allow other work to be done on the UI thread, posting back the rest of the functions processing to be done after other messages waiting to be processed:

    public async void button1_Click(...)
    {
        int counter = 0;
        foreach(var item in data)
        {
            var result = Process(item);
            listBox1.Items.Add(result);
            if ((++counter % 10) == 0) await Task.Yield();
        }
    }

    I mention this is simlar to DoEvents, but it has an important difference.  DoEvents installs a new message loop, whereas Yield yields back to the existing one.

    Task.Yield may also be used in server scenarios.  Let's say you had a long-running function to run on the ThreadPool.  You might not want to tie up a ThreadPool thread for the duration of your functions execution, thereby potentially starving other work items in the pool and/or forcing the pool to introduce more threads.  You could thus Yield within your function, which will cause the rest of your function's execution to be queued as a new work item.

    I hope that helps.

    Tuesday, December 7, 2010 5:53 PM
    Moderator

All replies

  • Hi iron9light-

    Conceptually, Yield enables you to temporarily leave the current async method to allow other things to be done on the underlying thread.  Technically, Yield works by posting the rest of the method back to the "current context", where the current context is defined to be the current SynchronizationContext if there is one, or else the current TaskScheduler (which if you're not inside of a task defaults to TaskScheduler.Default).

    The most common usage of Yield would be in places where you may have previously wanted to use something like Application.DoEvents.  Let's say you were writing a UI button click handler:

    public void button1_Click(...)
    {
        foreach(var item in data)
        {
            var result = Process(item);
            listBox1.Items.Add(result);
        }
    }

    If the data list is small, this may be fine to do on the UI thread, but if data has any chance of being large, this foreach loop could end up blocking the UI thread for an unacceptable period of time.  You can thus "Yield" out of it to allow other work to be done on the UI thread, posting back the rest of the functions processing to be done after other messages waiting to be processed:

    public async void button1_Click(...)
    {
        int counter = 0;
        foreach(var item in data)
        {
            var result = Process(item);
            listBox1.Items.Add(result);
            if ((++counter % 10) == 0) await Task.Yield();
        }
    }

    I mention this is simlar to DoEvents, but it has an important difference.  DoEvents installs a new message loop, whereas Yield yields back to the existing one.

    Task.Yield may also be used in server scenarios.  Let's say you had a long-running function to run on the ThreadPool.  You might not want to tie up a ThreadPool thread for the duration of your functions execution, thereby potentially starving other work items in the pool and/or forcing the pool to introduce more threads.  You could thus Yield within your function, which will cause the rest of your function's execution to be queued as a new work item.

    I hope that helps.

    Tuesday, December 7, 2010 5:53 PM
    Moderator
  • Dont know if it fits here, but I try ;)

    Playing around with CTP2, I thought of creating a "block-reader" - bc it might be helpfull, but basically because it seems as a nice exercise.

    Say I would like to achieve something like:

    Private sub foo
    
    Dim br As New BlockReader(1024) ' passing the blocksize I like to use
    
    For each block as Byte() in br.Blocks
    
    ' do something
    
    Next
    
    end sub
    
    

    Of course I want it asynchronous. To get it iteratable, Blocks must be an Iterator Property. But how do I get an non blocking read (with the new async features) inside that property? I can't say that Block is an Async property, bc Async is (currently) an extension method and therefore can not be applied to properties. But if it's not async, I cant use await.

    My initial idea was something like

    yield await or await yield and Task(Of byte()).

    Having watched the videos about the "technical" background, I understand that this might not make much sense anyway, bc yield and await are more or less "identical". So I assume that I am on the wrong track anyway. But what's the right path? Is Taskex.Yield something that leads into the right direction? Or ist it just some problem NOW, because in the next VS release async could be applied to properties?

    Thursday, July 21, 2011 9:23 AM
  • Hello, Picoflop, and welcome to the MSDN forums!

    Generally, it is best to start a new thread when asking a different question; if your question is related to a previous question, you may include a link to the previous question in your question. This helps out future searchers.

    It sounds like what you want is IAsyncEnumerable (actually part of Rx). TaskEx.Yield is conceptually closer to a "thread yield" than an "enumerable yield".

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible
    Friday, July 22, 2011 12:29 AM
  • Thanks for your answer Stephen.

    I guess I'd prefer to stay within the Async CTP and FW only and not use the RX extensions ;) I'll create a new thread when I have some code to explain more clearly what I want to achieve and to ask IF it could be done (without rx).

     

    Regarding taskex.yield -> If I understand Stephen correctly, it's a "kind of" DoEvents. Why is it named "yield" then and not something like "ContinueEvents" - which would fit better (IMHO, but english is not my native language, so please be patient) what Stephen described?

    Friday, July 22, 2011 3:23 PM
  • Regarding taskex.yield -> If I understand Stephen correctly, it's a "kind of" DoEvents. Why is it named "yield" then and not something like "ContinueEvents" - which would fit better (IMHO, but english is not my native language, so please be patient) what Stephen described?


    What Stephen meant is that Yield is more of a "pause" or "resume later" - i.e., you are yielding the execution of your method. This use of the "Yield" term is consistent with other uses, such as Thread.Yield (which yields the thread of execution).

    As far as DoEvents goes, Yield does serve a similar purpose, but DoEvents has a strong negative vibe around it. Do a search for "DoEvents is evil" and you'll see what I mean; the problem is that DoEvents starts a nested message loop. (WPF also supports nested message loops but it's much harder to do - so you have to really try to do things wrong). Yield does not use nested message loops; it causes the async function to yield back to its caller and then resume later.

    Even if DoEvents didn't have a negative vibe, I would think Yield describes the meaning better than ContinueEvents (which would imply that events were suspended at some point in the past). IMO. :)

           -Steve


    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
      and How to Implement IDisposable and Finalizers: 3 Easy Rules
    Microsoft Certified Professional Developer

    How to get to Heaven according to the Bible
    Friday, July 22, 2011 3:51 PM
  • There is another vital reason I ran into for using Yield before a long-running function (or even an outright kernel block). When a Task (A) marks a Task (B) as completed, there's a chance that Task (C) awaiting on Task (B) will be run directly inline, deeper into the callstack. If Task (C) then blocks, Task (A) will never return from the call to Task.SetResult(). When using async synchronization primitives to protect thread-blocking resources, you can end up with the most curiously godawful deadlocks unless you Yield first.
    Wednesday, September 5, 2012 6:26 PM