When would I use Task.Yield()?
When you use async
/await
, there is no guarantee that the method you call when you do await FooAsync()
will actually run asynchronously. The internal implementation is free to return using a completely synchronous path.
If you're making an API where it's critical that you don't block and you run some code asynchronously, and there's a chance that the called method will run synchronously (effectively blocking), using await Task.Yield()
will force your method to be asynchronous, and return control at that point. The rest of the code will execute at a later time (at which point, it still may run synchronously) on the current context.
This can also be useful if you make an asynchronous method that requires some "long running" initialization, ie:
private async void button_Click(object sender, EventArgs e)
{
await Task.Yield(); // Make us async right away
var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later
await UseDataAsync(data);
}
Without the Task.Yield()
call, the method will execute synchronously all the way up to the first call to await
.
Task.Yield - real usages?
When you see:
await Task.Yield();
you can think about it this way:
await Task.Factory.StartNew(
() => {},
CancellationToken.None,
TaskCreationOptions.None,
SynchronizationContext.Current != null?
TaskScheduler.FromCurrentSynchronizationContext():
TaskScheduler.Current);
All this does is makes sure the continuation will happen asynchronously in the future. By asynchronously I mean that the execution control will return to the caller of the async
method, and the continuation callback will not happen on the same stack frame.
When exactly and on what thread it will happen completely depends on the caller thread's synchronization context.
For a UI thread, the continuation will happen upon some future iteration of the message loop, run by Application.Run
(WinForms) or Dispatcher.Run
(WPF). Internally, it comes down to the Win32 PostMessage
API, which post a custom message to the UI thread's message queue. The await
continuation callback will be called when this message gets pumped and processed. You're completely out of control about when exactly this is going to happen.
Besides, Windows has its own priorities for pumping messages: INFO: Window Message Priorities. The most relevant part:
Under this scheme, prioritization can be considered tri-level. All
posted messages are higher priority than user input messages because
they reside in different queues. And all user input messages are
higher priority than WM_PAINT and WM_TIMER messages.
So, if you use await Task.Yield()
to yield to the message loop in attempt to keep the UI responsive, you are actually at risk of obstructing the UI thread's message loop. Some pending user input messages, as well as WM_PAINT
and WM_TIMER
, have a lower priority than the posted continuation message. Thus, if you do await Task.Yield()
on a tight loop, you still may block the UI.
This is how it is different from the JavaScript's setTimer
analogy you mentioned in the question. A setTimer
callback will be called after all user input message have been processed by the browser's message pump.
So, await Task.Yield()
is not good for doing background work on the UI thread. In fact, you very rarely need to run a background process on the UI thread, but sometimes you do, e.g. editor syntax highlighting, spell checking etc. In this case, use the framework's idle infrastructure.
E.g., with WPF you could do await Dispatcher.Yield(DispatcherPriority.ApplicationIdle)
:
async Task DoUIThreadWorkAsync(CancellationToken token)
{
var i = 0;
while (true)
{
token.ThrowIfCancellationRequested();
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
// do the UI-related work item
this.TextBlock.Text = "iteration " + i++;
}
}
For WinForms, you could use Application.Idle
event:
// await IdleYield();
public static Task IdleYield()
{
var idleTcs = new TaskCompletionSource<bool>();
// subscribe to Application.Idle
EventHandler handler = null;
handler = (s, e) =>
{
Application.Idle -= handler;
idleTcs.SetResult(true);
};
Application.Idle += handler;
return idleTcs.Task;
}
It is recommended that you do not exceed 50ms for each iteration of such background operation running on the UI thread.
For a non-UI thread with no synchronization context, await Task.Yield()
just switches the continuation to a random pool thread. There is no guarantee it is going to be a different thread from the current thread, it's only guaranteed to be an asynchronous continuation. If ThreadPool
is starving, it may schedule the continuation onto the same thread.
In ASP.NET, doing await Task.Yield()
doesn't make sense at all, except for the workaround mentioned in @StephenCleary's answer. Otherwise, it will only hurt the web app performance with a redundant thread switch.
So, is await Task.Yield()
useful? IMO, not much. It can be used as a shortcut to run the continuation via SynchronizationContext.Post
or ThreadPool.QueueUserWorkItem
, if you really need to impose asynchrony upon a part of your method.
Regarding the books you quoted, in my opinion those approaches to using Task.Yield
are wrong. I explained why they're wrong for a UI thread, above. For a non-UI pool thread, there's simply no "other tasks in the thread to execute", unless you running a custom task pump like Stephen Toub's AsyncPump
.
Updated to answer the comment:
... how can it be asynchronouse operation and stay in the same thread
?..
As a simple example: WinForms app:
async void Form_Load(object s, object e)
{
await Task.Yield();
MessageBox.Show("Async message!");
}
Form_Load
will return to the caller (the WinFroms framework code which has fired Load
event), and then the message box will be shown asynchronously, upon some future iteration of the message loop run by Application.Run()
. The continuation callback is queued with WinFormsSynchronizationContext.Post
, which internally posts a private Windows message to the UI thread's message loop. The callback will be executed when this message gets pumped, still on the same thread.
In a console app, you can run a similar serializing loop with AsyncPump
mentioned above.
Explain await Task.Yield in an ASPNET Core WebApi context
I'm expecting "End DoSomethingAsync" to get logged first and "DoSomethingAsync: Completed" to come a second later
That is an incorrect expectation; the await
here:
_logger.LogInformation("GET: Start DoSomethingAsync");
await DoSomethingAsync();
_logger.LogInformation("GET: End DoSomethingAsync");
means that the 3rd line doesn't happen until everything in DoSomethingAsync()
has completed asynchronously if needed. You can get the behaviour you want via:
_logger.LogInformation("GET: Start DoSomethingAsync");
var pending = DoSomethingAsync();
_logger.LogInformation("GET: End DoSomethingAsync");
await pending;
however, this is fundamentally the same as adding a second thread in regular non-async code - you now have two execution flows active on the same request. This can be valid, but can also be dangerous if thread-safety semantics are not observed.
await Task.Yield() and its alternatives
One situation where Task.Yield()
is actually useful is when you are await
recursively-called synchronously-completed Task
s. Because csharp’s async
/await
“releases Zalgo” by running continuations synchronously when it can, the stack in a fully synchronous recursion scenario can get big enough that your process dies. I think this is also partly due to tail-calls not being able to be supported because of the Task
indirection. await Task.Yield()
schedules the continuation to be run by the scheduler rather than inline, allowing growth in the stack to be avoided and this issue to be worked around.
Also, Task.Yield()
can be used to cut short the synchronous portion of a method. If the caller needs to receive your method’s Task
before your method performs some action, you can use Task.Yield()
to force returning the Task
earlier than would otherwise naturally happen. For example, in the following local method scenario, the async
method is able to get a reference to its own Task
safely (assuming you are running this on a single-concurrency SynchronizationContext
such as in winforms or via nito’s AsyncContext.Run()
):
using Nito.AsyncEx;
using System;
using System.Threading.Tasks;
class Program
{
// Use a single-threaded SynchronizationContext similar to winforms/WPF
static void Main(string[] args) => AsyncContext.Run(() => RunAsync());
static async Task RunAsync()
{
Task<Task> task = null;
task = getOwnTaskAsync();
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync()
{
// Cause this method to return and let the 「task」 local be assigned.
await Task.Yield();
return task;
}
}
}
output:
3 == 3: True
I am sorry that I cannot think up any real-life scenarios where being able to forcibly cut short the synchronous portion of an async
method is the best way to do something. Knowing that you can do a trick like I just showed can be useful sometimes, but it tends to be more dangerous too. Often you can pass around data in a better, more readable, and more threadsafe way. For example, you can pass the local method a reference to its own Task
using a TaskCompletionSource
instead:
using System;
using System.Threading.Tasks;
class Program
{
// Fully free-threaded! Works in more environments!
static void Main(string[] args) => RunAsync().Wait();
static async Task RunAsync()
{
var ownTaskSource = new TaskCompletionSource<Task>();
var task = getOwnTaskAsync(ownTaskSource.Task);
ownTaskSource.SetResult(task);
var foundTask = await task;
Console.WriteLine($"{task?.Id} == {foundTask?.Id}: {task == foundTask}");
async Task<Task> getOwnTaskAsync(
Task<Task> ownTaskTask)
{
// This might be clearer.
return await ownTaskTask;
}
}
}
output:
2 == 2: True
What's the difference between Task.Yield, Task.Run, and ConfigureAwait(false)?
Task.Yield
is not a replacement for Task.Run
and it has nothing to do with Task.ConfigureAwait
.
Task.Yield
- Generates an awaitable that completes just after it is checked for completion.ConfigureAwait(false)
- Generates an awaitable from a task that disregards the capturedSynchronizationContext
.Task.Run
- Executes a delegate on aThreadPool
thread.
Task.Yield
is different than ConfigureAwait
in that it is an awaitable all by itself, and not a configurable wrapper of another awaitable (i.e. the Task
). Another difference is that Task.Yield
does continue on the captured context.
Task.Run
is different then both as it just takes a delegate and runs it on the ThreadPool
, you can use it with ConfigureAwait(false)
or without.
Task.Yield
should be used to force an asynchronous point, not as a replacement to Task.Run
. When await is reached in an async method it checks whether the task (or other awaitable) already completed and if so it continues on synchronously. Task.Yield
prevents that from happening so it's useful for testing.
Another usage is in UI methods, where you don't want to be hogging the single UI thread, you insert an asynchronous point and the rest is scheduled to be executed in a later time.
Task.Yield() versus Task.Delay(0)
Inside Task.Delay
, it looks like this (the single parameter (int)
version just calls the below version):
[__DynamicallyInvokable]
public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
{
if (millisecondsDelay < -1)
{
throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay"));
}
if (cancellationToken.IsCancellationRequested)
{
return FromCancellation(cancellationToken);
}
if (millisecondsDelay == 0)
{
return CompletedTask;
}
DelayPromise state = new DelayPromise(cancellationToken);
if (cancellationToken.CanBeCanceled)
{
state.Registration = cancellationToken.InternalRegisterWithoutEC(delegate (object state) {
((DelayPromise) state).Complete();
}, state);
}
if (millisecondsDelay != -1)
{
state.Timer = new Timer(delegate (object state) {
((DelayPromise) state).Complete();
}, state, millisecondsDelay, -1);
state.Timer.KeepRootedWhileScheduled();
}
return state;
}
As you can hopefully see:
if (millisecondsDelay == 0)
{
return CompletedTask;
}
Which means it always returns a completed task, and therefore your code will always continue running past that particular await
line.
Task.Yield(); SyncAction(); vs Task.Run(()=SyncAction());
While both options are bad, there is a difference (important in GUI applications): after Task.Yield
returns, the rest of the method will be dispatched back to original SynchronizationContext
. If you ran that from UI thread, your long running operation will be executed on UI thread, thus freezing it. So if your intention was to avoid UI blocking - Task.Yield
won't work. With Task.Run
that won't happen.
Is calling Task.Yield in Asp.Net Core ensuring continuation to run on the ThreadPool?
Assuming an Asp.Net Core application which does not have a SynchronizationContext, is there any functional difference from awaiting Task.Yield and passing that continuation to Task.Run?
There's practically no difference. await Task.Yield
will queue the continuation to the thread pool and return to the caller. await Task.Run
will queue its delegate to the thread pool and return to the caller.
Personally, I prefer Task.Run
because it has a very explicit intent and because it's unambiguous (i.e., in other scenarios such as GUI threads, await Task.Yield
will not queue to the thread pool, while await Task.Run
always does the same thing regardless of context).
As others have noted in the comments, there's no reason to actually do either one on ASP.NET Core, which is already running on thread pool threads and does not have a request context. It would add a little overhead to the request while providing no benefit at all.
When is it asynchronous and when is synchronous
This answer contains general info and advices, and it's not focused on the code that you posted.
- Being confused while learning async-await is OK. Getting a perfect understanding of async-await without going through a confusion phase is practically almost impossible IMHO.
- The
await Task.Yield()
,await Task.Delay(1)
,await Task.Run(() => { })
, and.ConfigureAwait(false)
are dirty hacks that people sometimes use out of frustration that an explicit way to switch imperatively to theThreadPool
context does not exist (in the standard libraries). ASwitchTo
method existed in some pre-prelease .NET version, then it was removed for technical reasons, then the technical reasons were eliminated, reintroducing the method never became a high enough priority, and so it didn't happen. It may happen in the future, but don't hold your breath. It is currently available in the Microsoft.VisualStudio.Threading package, if you want it. - You don't need any of these hacks to write async-await code successfully. If you want to offload work to the
ThreadPool
, there is theTask.Run
method that does the job perfectly. - Offloading work to the
ThreadPool
should be done at the "application code" level, not at the "library code" level. You can read this article to understand why exposing asynchronous wrappers for synchronous methods is not a good idea.
Difference between using Task.Yield() in a User Interface and Console App
Watching what happens with the debugger, the process gets to await
Task.Yield() and never progresses to return form.ShowDialog() and thus
you never see the RunningForm. The process then goes to
LoadDataAsync() and hangs forever on await Task.Delay(2000).Why is this happening?
What happens here is that when you do var runningForm = new RunningForm()
on a console thread without any synchronization context (System.Threading.SynchronizationContext.Current
is null), it implicitly creates an instance of WindowsFormsSynchronizationContext
and installs it on the current thread, more on this here.
Then, when you hit await Task.Yield()
, the ShowDialogAsync
method returns to the caller and the await
continuation is posted to that new synchronization context. However, the continuation never gets a chance to be invoked, because the current thread doesn't run a message loop and the posted messages don't get pumped. There isn't a deadlock, but the code after await Task.Yield()
is never executed, so the dialog doesn't even get shown. The same is true about await Task.Delay(2000)
.
I'm more interested in learning why it works for WinForms and not for
Console Applications.
You need a UI thread with a message loop in your console app. Try refactoring your console app like this:
public void Run()
{
var runningForm = new RunningForm();
runningForm.Loaded += async delegate
{
runningForm.ShowRunning();
var progressFormTask = runningForm.ShowDialogAsync();
var data = await LoadDataAsync();
runningForm.Close();
await progressFormTask;
MessageBox.Show(data.ToString());
};
System.Windows.Forms.Application.Run(runningForm);
}
Here, the job of Application.Run
is to start a modal message loop (and install WindowsFormsSynchronizationContext
on the current thread) then show the form. The runningForm.Loaded
async event handler is invoked on that synchronization context, so the logic inside it should work just as expected.
That however makes Test.Run
a synchronous method, i. e., it only returns when the form is closed and the message loop has ended. If this is not what you want, you'd have to create a separate thread to run your message loop, something like I do with MessageLoopApartment
here.
That said, in a typical WinForms or WPF application you should almost never need a secondary UI thread.
Related Topics
How to Determine the Standard Deviation (Stddev) of a Set of Values
How to Return PDF to Browser in MVC
Adding Distance to a Gps Coordinate
How to Log the Generated SQL from Dbcontext.Savechanges() in My Program
How to Determine If a Decimal/Double Is an Integer
How to Implement Generic Repository Design Pattern with Dapper
.Net Core 2.1 Identity Get All Users with Their Associated Roles
Is There a Difference Between I==0 and 0==I
Uploadfile with Post Values by Webclient
C++/Cli Wrapper for Native C++ to Use as Reference in C#
What Are the Definitions for Lparam and Wparam
HTML Table (Text) to Image Using C#
Using C# to Dynamically Generate CSS Files
How to Marshal a Struct That Contains a Variable-Sized Array to C#