This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Task. Yield Method

Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

Creates an awaitable task that asynchronously yields back to the current context when awaited.

A context that, when awaited, will asynchronously transition back into the current context at the time of the await. If the current SynchronizationContext is non-null, it is treated as the current context. Otherwise, the task scheduler that is associated with the currently executing task is treated as the current context.

You can use await Task.Yield(); in an asynchronous method to force the method to complete asynchronously. If there is a current synchronization context ( SynchronizationContext object), this will post the remainder of the method's execution back to that context. However, the context will decide how to prioritize this work relative to other work that may be pending. The synchronization context that is present on a UI thread in most UI environments will often prioritize work posted to the context higher than input and rendering work. For this reason, do not rely on await Task.Yield(); to keep a UI responsive. For more information, see the entry Useful Abstractions Enabled with ContinueWith in the Parallel Programming with .NET blog.

Additional resources

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

When would I use Task.Yield()?

I'm using async/await and Task a lot but have never been using Task.Yield() and to be honest even with all the explanations I do not understand why I would need this method.

Can somebody give a good example where Yield() is required?

Cosmin's user avatar

5 Answers 5

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:

Without the Task.Yield() call, the method will execute synchronously all the way up to the first call to await .

Reed Copsey's user avatar

Internally, await Task.Yield() simply queues the continuation on either the current synchronization context or on a random pool thread, if SynchronizationContext.Current is null .

It is efficiently implemented as custom awaiter. A less efficient code producing the identical effect might be as simple as this:

Task.Yield() can be used as a short-cut for some weird execution flow alterations. For example:

That said, I can't think of any case where Task.Yield() cannot be replaced with Task.Factory.StartNew w/ proper task scheduler.

"await Task.Yield()" and its alternatives

Task.Yield - real usages?

noseratio's user avatar

One use of Task.Yield() is to prevent a stack overflow when doing async recursion. Task.Yield() prevents syncronous continuation. Note, however, that this can cause an OutOfMemory exception (as noted by Triynko). Endless recursion is still not safe and you're probably better off rewriting the recursion as a loop.

Joakim M. H.'s user avatar

Task.Yield() is like a counterpart of Thread.Yield() in async-await but with much more specific conditions. How many times do you even need Thread.Yield() ? I will answer the title "when would you use Task.Yield() " broadly first. You would when the following conditions are fulfilled:

The term "async context" here means " SynchronizationContext first then TaskScheduler ". It was used by Stephen Cleary .

Task.Yield() is approximately doing this (many posts get it slightly wrong here and there):

If any one of the conditions is broken, you need to use other alternatives instead.

If the continuation of a task should be in Task.DefaultScheduler , you normally use ConfigureAwait(false) . On the contrary, Task.Yield() gives you an awaitable not having ConfigureAwait(bool) . You need to use the approximated code with TaskScheduler.Default .

If Task.Yield() obstructs the queue, you need to restructure your code instead as explained by noseratio .

If you need the continuation to happen much later, say, in the order of millisecond, you would use Task.Delay .

If you want the task to be cancellable in the queue but do not want to check the cancellation token nor throw an exception yourself, you need to use the approximated code with a cancellation token.

Task.Yield() is so niche and easily dodged. I only have one imaginary example by mixing my experience. It is to solve an async dining philosopher problem constrained by a custom scheduler . In my multi-thread helper library InSync , it supports unordered acquisitions of async locks. It enqueues an async acquisition if the current one failed. The code is here . It needs ConfigureAwait(false) as a general purpose library so I need to use Task.Factory.StartNew . In a closed source project, my program needs to execute significant synchronous code mixed with async code with

Thus, I need a custom scheduler . I could easily imagine some poor developers somehow need to mix sync and async code together with some special schedulers in a parallel universe (one universe probably does not contain such developers); but why wouldn't they just use the more robust approximated code so they do not need to write a lengthy comment to explain why and what it does?

Theodor Zoulias's user avatar

Task.Yield() may be used in mock implementations of async methods.

Stephen Kennedy's user avatar

Your Answer

Sign up or log in, post as a guest.

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service , privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged c# async-await or ask your own question .

Hot Network Questions

what is task yield

Your privacy

By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy .

Duong's Blog

About technology stuff that piqued my interest

Note: phiên bản Tiếng Việt của bài này ở link dưới.

https://duongnt.com/task-yield-vie

Task.Yield - a rarely used method in C#

Task.Yield is just one among many methods of the Task class. Compared to its siblings , we certainly use it much less frequently. However, there are still cases where it is helpful.

You can download the sample code in this article from the link below.

https://github.com/duongntbk/TaskYieldDemo

What does the Task.Yield method do?

Below is the definition of the Task.Yield method.

Creates an awaitable task that asynchronously yields back to the current context when awaited.

It is somewhat similar to the Task.FromResult method, or the Task.CompletedTask property. When awaited, they all finish immediately. But let’s take a look at the code below.

And this is the result when we use Task.Yield .

The difference is Task.Yield can force an asynchronous method to complete asynchronously. But what exactly does that mean? And how does that help us? We will find out in the next few sections.

When synchronicity is not good

A somewhat contrived example.

Let’s consider the method below. It keeps printing a log message and invoking the provided delegate until we stop it via a CancellationToken .

Now, we will let that method run for 2 seconds before cancelling it.

The code above works correctly when we pass an asynchronous handler.

Will print the following.

What if the delegate returns a completed task?

Next, we try passing a new delegate that always returns a completed task.

Suddenly, our program runs into an infinite loop.

Just because we have an asynchronous method does not guarantee that it will run asynchronously. Instead, the runtime will execute our method synchronously until it encounters the first await statement that is not completed yet. Only then will our method "yield" control back to the current context.

When we pass asyncHandler into DriverMethod , our method yields control when it reaches the command await handler() inside RunUntilCancelAsync . At that time, we continue executing the rest of DriverMethod .

But things are different when we pass completedTaskHandler into DriverMethod . When we reach the command await handler() , because Task.CompletedTask is already completed, the runtime keeps executing RunUntilCancelAsync synchronously and doesn’t yield control back to the current context. This means we never reach the line cts.Cancel() in DriverMethod . However, without cancelling the token, the loop in RunUntilCancelAsync will never stop. We have created an infinite loop.

What if we create a delegate with Task.Yield?

Below is another delegate that uses Task.Yield .

We can verify that there is no infinite loop.

Each time we call await on taskYieldHandler , it finishes immediately. This explains why we see so many log messages before RunUntilCancelAsync is stopped. But unlike in the previous section, we do yield control back to the current context. Thanks to that, we can cancel the token after 2 seconds.

Task.Yield in unit testing

The only real life application of Task.Yield I can find is in unit testing.

A simple BackgroundService class

Extending the BackgroundService class is a simple way to create long running services in C# .NET Core (please see this link for more information). This class in the sample project is a service to retrieve current weather statuses and write them to the console in a loop.

Below is the InitializeAsync method of the WeatherRetriever class. It retrieves data using an IDataReader and writes data using an IDataWriter . Those interfaces are used in a loop until the stoppingToken is cancelled.

And because we care about our software’s quality, we will write unit tests for this method. While doing so, it’s necessary to mock out the IDataReader and IDataWriter objects.

A naive approach to testing InitializeAsync

You can find the complete test case here . We will let InitializeAsync run for 200 ms and then stop it by cancelling the token. And we mock IDataReader and IDataWriter with FakeItEasy .

But if you try to run this test, you’ll find that it will never stop. The reason is exactly the same as in the case of the RunUntilCancelAsync method. When called, the mock objects created by FakeItEasy return a value right away. Because of that, InitializeAsync keeps running synchronously forever in a loop. Which means we never yield control back to the current context, and we never reach this line cts.Cancel() to cancel the token.

Task.Yield to the rescue

It’s actually quite simple to fix the issue in the code above. We only need to make InitializeAsync yield back to the current context inside the loop. And that sounds like the perfect job for Task.Yield . Below is how we force IDataReader.ReadAsync to complete asynchronously.

We pass an asynchronous delegate into the ReturnsLazily method of FakeItEasy . And inside that delegate, we await Task.Yield to force the control back to the current context. Thanks to that, we can reach the code to cancel the token and stop InitializeAsync .

You can find the full code for the new test here . If you try to run it, you can verify that it will pass.

Task.Yield is one of those features that don’t seem to be all that useful but still have their niches. Aside from using Task.Yield , there is another way to cancel the token while testing InitializeAsync . Can you find it?

Share this:

' src=

One Thought on “Task.Yield – a rarely used method in C#”

Leave a Reply Cancel reply

This page requires JavaScript.

Please turn on JavaScript in your browser and refresh the page to view its content.

Cloud as a Story - Vunvulea Radu

DREAMER, CRAFTER, TECHNOLOGY ENTHUSIAST, SPEAKER, TRAINER, AZURE MVP, SOLVING HARD BUSINESS PROBLEMS WITH CUTTING-EDGE TECHNOLOGY

Search This Blog

Task.yield(...), task.delay(...).

Do not rely on await Task.Yield() to keep a UI responsive! http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.yield.aspx

Post a Comment

Popular posts from this blog, azure ad and aws cognito side-by-side, windows docker containers can make win32 api calls, use com and asp.net webforms, what to do when you hit the throughput limits of azure storage (blobs).

A Tour of Task, Part 10: Promise Tasks

Last time, we looked at ways to start Delegate Tasks . Today we’ll look at the most common ways to create Promise Tasks. As a reminder, Promise Tasks are tasks that represent a kind of “event” within a system; they don’t have any user-defined code to execute.

Task.Delay is the asynchronous equivalent of Thread.Sleep .

The int argument is treated as a number of milliseconds; I usually prefer the TimeSpan versions since they are more explicit. Using int millisecond values for timeouts is a holdover from an older API design; many Win32-level APIs only take timeout values as integer milliseconds. So, it makes sense to expose an int parameter for lower-level waits like WaitHandle.WaitOne or even Task.Wait . However, Task.Delay isn’t a thin wrapper over any Win32 API; the int parameter in this case is just provided for tradition.

Delay may also take a CancellationToken , which allows the delay to be cancelled.

Under the hood, Delay starts a timer and completes its returned task when that timer fires. Or, if the CancellationToken is signaled first, then Delay cancels its returned task.

In real-world code, Delay is almost never used. Its primary use case is as a retry timeout, i.e., if an asynchronous operation failed, the code will (asynchronously) wait a period of time before trying again. Generally, retry logic is wrapped into a separate library (such as Transient Fault Handling or Polly ), and Delay is only used internally by those libraries, not directly by application code.

Task.Yield has several interesting aspects. To begin with, it doesn’t actually return a Task , so it’s not really a Promise Task kind of method:

But it does kind of act kind of like a Promise Task. The YieldAwaitable type interacts with the async compiler transformation to force an asynchronous point within a method. By default, if await is used on an operation that has already completed, then the execution of the async method continues synchronously. YieldAwaitable throws a wrench into this by always claiming it is not completed, and then scheduling its continuations immediately. This causes await to schedule the rest of the async method for immediate execution and return.

I’ve used Task.Yield only occasionally during unit testing, when I needed to ensure that a particular method would in fact work if its asynchronous operation did not complete synchronously. I’ve found Yield most useful when the asynchronous operation in question normally does complete synchronously, and I need to force asynchrony to ensure the method behavior is correct.

However, I’ve never needed Yield in production code. There is one use case where developers sometimes (incorrectly) attempt to use Yield : to try to “refresh” the UI.

However, this approach will not work. The reason is that UI message loops are priority queues, and any scheduled continuations have a much higher priority than “repaint the window”. So, the Yield schedules the continuation and returns to the message loop, and the message loop immediately executes that continuation without processing any of its WM_PAINT messages.

Some developers have discovered that using Task.Delay instead of Task.Yield will allow message processing (messages are processed until the timer fires). However, a far cleaner solution is to do the CPU-bound work on a background thread:

In conclusion, Task.Yield is occasionally useful when unit testing, but much less so for production code.

Task.FromResult

Task.FromResult will create a completed task with the specified value:

It might seem silly at first to return a task that is already completed, but this is actually useful in several scenarios.

For instance, an interface method may have an asynchronous (task-returning) signature, and if an implementation is synchronous, then it can use Task.FromResult to wrap up its (synchronous) result within a task. This is particularly useful when creating stubs for unit testing, but is also occasionally useful in production code:

Be careful, though, that your synchronous implementation is not blocking . Implementing an asynchronous API with a blocking method is surprising behavior.

Another use case of Task.FromResult is when doing some form of caching. In this case, you have some data that is synchronously retrieved (from the cache), and need to return it directly. In the case of a cache miss, then a true asynchronous operation is performed:

Tip: If you can, cache the task objects themselves instead of their resulting values; maintain a cache of operations rather than results .

As of this writing, one final common use of Task.FromResult is just as a completed task. For this, the expressions Task.FromResult(0) or Task.FromResult<object>(null) are commonly used. This use case is similar to the synchronous implementation of an asynchronous API:

In the preview builds of .NET 4.6, there is a static Task.CompletedTask that should be used instead of Task.FromResult(0) or Task.FromResult<object>(null) .

You might be wondering if there’s a way to return already-completed tasks in other states - particularly, canceled or faulted tasks. As of now, you have to write this yourself (using TaskCompletionSource ), but .NET 4.6 will introduce the Task.FromCanceled and Task.FromException methods to return synchronously canceled or faulted tasks.

what is task yield

Elixir

Task (Elixir v1.12.3) View Source

Conveniences for spawning and awaiting tasks.

Tasks are processes meant to execute one particular action throughout their lifetime, often with little or no communication with other processes. The most common use case for tasks is to convert sequential code into concurrent code by computing a value asynchronously:

Tasks spawned with async can be awaited on by their caller process (and only their caller) as shown in the example above. They are implemented by spawning a process that sends a message to the caller once the given computation is performed.

Besides async/1 and await/2 , tasks can also be started as part of a supervision tree and dynamically spawned on remote nodes. We will explore these scenarios next.

async and await

One of the common uses of tasks is to convert sequential code into concurrent code with Task.async/1 while keeping its semantics. When invoked, a new process will be created, linked and monitored by the caller. Once the task action finishes, a message will be sent to the caller with the result.

Task.await/2 is used to read the message sent by the task.

There are two important things to consider when using async :

If you are using async tasks, you must await a reply as they are always sent. If you are not expecting a reply, consider using Task.start_link/1 detailed below.

async tasks link the caller and the spawned process. This means that, if the caller crashes, the task will crash too and vice-versa. This is on purpose: if the process meant to receive the result no longer exists, there is no purpose in completing the computation.

If this is not desired, you will want to use supervised tasks, described next.

Dynamically supervised tasks

The Task.Supervisor module allows developers to dynamically create multiple supervised tasks.

A short example is:

However, in the majority of cases, you want to add the task supervisor to your supervision tree:

And now you can use async/await once again passig the name of the supervisor isntead of the pid:

We encourage developers to rely on supervised tasks as much as possible. Supervised tasks enable a huge variety of patterns which allows you explicit control on how to handle the results, errors, and timeouts. Here is a summary:

Use Task.Supervisor.start_child/2 to start a fire-and-forget task and you don't care about its results nor about if it completes successfully

Use Task.Supervisor.async/2 + Task.await/2 allows you to execute tasks concurrently and retrieve its result. If the task fails, the caller will also fail

Use Task.Supervisor.async_nolink/2 + Task.yield/2 + Task.shutdown/2 allows you to execute tasks concurrently and retrieve their results or the reason they failed within a given time frame. If the task fails, the caller won't fail: you will receive the error reason either on yield or shutdown

See the Task.Supervisor module for details on the supported operations.

Distributed tasks

Since Elixir provides a Task.Supervisor , it is easy to use one to dynamically start tasks across nodes:

Note that, when working with distributed tasks, one should use the Task.Supervisor.async/4 function that expects explicit module, function, and arguments, instead of Task.Supervisor.async/2 that works with anonymous functions. That's because anonymous functions expect the same module version to exist on all involved nodes. Check the Agent module documentation for more information on distributed processes as the limitations described there apply to the whole ecosystem.

Statically supervised tasks

The Task module implements the child_spec/1 function, which allows it to be started directly under a regular Supervisor - instead of a Task.Supervisor - by passing a tuple with a function to run:

This is often useful when you need to execute some steps while setting up your supervision tree. For example: to warm up caches, log the initialization status, etc.

If you don't want to put the Task code directly under the Supervisor , you can wrap the Task in its own module, similar to how you would do with a GenServer or an Agent :

And then passing it to the supervisor:

Since these tasks are supervised and not directly linked to the caller, they cannot be awaited on. By default, the functions Task.start and Task.start_link are for fire-and-forget tasks, where you don't care about the results or if it completes successfully or not.

use Task defines a child_spec/1 function, allowing the defined module to be put under a supervision tree. The generated child_spec/1 can be customized with the following options:

Opposite to GenServer , Agent and Supervisor , a Task has a default :restart of :temporary . This means the task will not be restarted even if it crashes. If you desire the task to be restarted for non-successful exits, do:

If you want the task to always be restarted:

See the "Child specification" section in the Supervisor module for more detailed information. The @doc annotation immediately preceding use Task will be attached to the generated child_spec/1 function.

Ancestor and Caller Tracking

Whenever you start a new process, Elixir annotates the parent of that process through the $ancestors key in the process dictionary. This is often used to track the hierarchy inside a supervision tree.

For example, we recommend developers to always start tasks under a supervisor. This provides more visibility and allows you to control how those tasks are terminated when a node shuts down. That might look something like Task.Supervisor.start_child(MySupervisor, task_specification) . This means that, although your code is the one who invokes the task, the actual ancestor of the task is the supervisor, as the supervisor is the one effectively starting it.

To track the relationship between your code and the task, we use the $callers key in the process dictionary. Therefore, assuming the Task.Supervisor call above, we have:

Which means we store the following relationships:

The list of callers of the current process can be retrieved from the Process dictionary with Process.get(:"$callers") . This will return either nil or a list [pid_n, ..., pid2, pid1] with at least one entry Where pid_n is the PID that called the current process, pid2 called pid_n , and pid2 was called by pid1 .

If a task crashes, the callers field is included as part of the log message metadata under the :callers key.

Link to this section Summary

The Task type.

The Task struct.

Starts a task that must be awaited on.

Returns a stream that runs the given function fun concurrently on each element in enumerable .

Returns a stream where the given function ( module and function_name ) is mapped concurrently on each element in enumerable .

Awaits a task reply and returns it.

Awaits replies from multiple tasks and returns them.

Returns a specification to start a task under a supervisor.

Unlinks and shuts down the task, and then checks for a reply.

Starts a task.

Starts a task as part of a supervision tree with the given fun .

Starts a task as part of a supervision tree with the given module , function , and args .

Temporarily blocks the current process waiting for a task reply.

Yields to multiple tasks in the given time interval.

Link to this section Types

See %Task{} for information about each field of the structure.

Link to this section Functions

It contains these fields:

:pid - the PID of the task process; nil if the task does not use a task process

:ref - the task monitor reference

:owner - the PID of the process that started the task

fun must be a zero-arity anonymous function. This function spawns a process that is linked to and monitored by the caller process. A Task struct is returned containing the relevant information. Developers must eventually call Task.await/2 or Task.yield/2 followed by Task.shutdown/2 on the returned task.

Read the Task module documentation for more information about the general usage of async tasks.

This function spawns a process that is linked to and monitored by the caller process. The linking part is important because it aborts the task if the parent process dies. It also guarantees the code before async/await has the same properties after you add the async call. For example, imagine you have this:

Now you want to make the heavy_fun() async:

As before, if heavy_fun/0 fails, the whole computation will fail, including the parent process. If you don't want the task to fail then you must change the heavy_fun/0 code in the same way you would achieve it if you didn't have the async call. For example, to either return {:ok, val} | :error results or, in more extreme cases, by using try/rescue . In other words, an asynchronous task should be thought of as an extension of a process rather than a mechanism to isolate it from all errors.

If you don't want to link the caller to the task, then you must use a supervised task with Task.Supervisor and call Task.Supervisor.async_nolink/2 .

In any case, avoid any of the following:

Setting :trap_exit to true - trapping exits should be used only in special circumstances as it would make your process immune to not only exits from the task but from any other processes.

Moreover, even when trapping exits, calling await will still exit if the task has terminated without sending its result back.

Unlinking the task process started with async / await . If you unlink the processes and the task does not belong to any supervisor, you may leave dangling tasks in case the parent dies.

async(module, function_name, args)

Similar to async/1 except the function to be started is specified by the given module , function_name , and args .

async_stream(enumerable, fun, options \\ [])

Works the same as async_stream/5 but with an anonymous function instead of a module-function-arguments tuple. fun must be a one-arity anonymous function.

Each enumerable element is passed as argument to the given function fun and processed by its own task. The tasks will be linked to the current process, similarly to async/1 .

Count the code points in each string asynchronously, then add the counts together using reduce.

See async_stream/5 for discussion, options, and more examples.

async_stream(enumerable, module, function_name, args, options \\ [])

Each element of enumerable will be prepended to the given args and processed by its own task. The tasks will be linked to an intermediate process that is then linked to the current process. This means a failure in a task terminates the current process and a failure in the current process terminates all tasks.

When streamed, each task will emit {:ok, value} upon successful completion or {:exit, reason} if the caller is trapping exits. The order of results depends on the value of the :ordered option.

The level of concurrency and the time tasks are allowed to run can be controlled via options (see the "Options" section below).

Consider using Task.Supervisor.async_stream/6 to start tasks under a supervisor. If you find yourself trapping exits to handle exits inside the async stream, consider using Task.Supervisor.async_stream_nolink/6 to start tasks that are not linked to the calling process.

:max_concurrency - sets the maximum number of tasks to run at the same time. Defaults to System.schedulers_online/0 .

:ordered - whether the results should be returned in the same order as the input stream. When the output is ordered, Elixir may need to buffer results to emit them in the original order. Setting this option to false disables the need to buffer at the cost of removing ordering. This is also useful when you're using the tasks only for the side effects. Note that regardless of what :ordered is set to, the tasks will process asynchronously. If you need to process elements in order, consider using Enum.map/2 or Enum.each/2 instead. Defaults to true .

:timeout - the maximum amount of time (in milliseconds or :infinity ) each task is allowed to execute for. Defaults to 5000 .

:on_timeout - what to do when a task times out. The possible values are:

Let's build a stream and then enumerate it:

The concurrency can be increased or decreased using the :max_concurrency option. For example, if the tasks are IO heavy, the value can be increased:

If you do not care about the results of the computation, you can run the stream with Stream.run/1 . Also set ordered: false , as you don't care about the order of the results either:

Attention: async + take

Given items in an async stream are processed concurrently, doing async_stream followed by Enum.take/2 may cause more items than requested to be processed. Let's see an example:

For a machine with 8 cores, the above will process 16 items instead of 10. The reason is that async_stream/5 always have 8 elements processing at once. So by the time Enum says it got all elements it needed, there are still 6 elements left to be processed.

The solution here is to use Stream.take/2 instead of Enum.take/2 to filter elements before-hand:

If for some reason you cannot take the elements before hand, you can use :max_concurrency to limit how many elements may be over processed at the cost of reducing concurrency.

await(task, timeout \\ 5000)

In case the task process dies, the current process will exit with the same reason as the task.

A timeout, in milliseconds or :infinity , can be given with a default value of 5000 . If the timeout is exceeded, then the current process will exit. If the task process is linked to the current process which is the case when a task is started with async , then the task process will also exit. If the task process is trapping exits or not linked to the current process, then it will continue to run.

This function assumes the task's monitor is still active or the monitor's :DOWN message is in the message queue. If it has been demonitored, or the message already received, this function will wait for the duration of the timeout awaiting the message.

This function can only be called once for any given task. If you want to be able to check multiple times if a long-running task has finished its computation, use yield/2 instead.

Compatibility with OTP behaviours

It is not recommended to await a long-running task inside an OTP behaviour such as GenServer . Instead, you should match on the message coming from a task inside your GenServer.handle_info/2 callback.

A GenServer will receive two messages on handle_info/2 :

{ref, result} - the reply message where ref is the monitor reference returned by the task.ref and result is the task result

{:DOWN, ref, :process, pid, reason} - since all tasks are also monitored, you will also receive the :DOWN message delivered by Process.monitor/1 . If you receive the :DOWN message without a a reply, it means the task crashed

Another consideration to have in mind is that tasks started by Task.async/1 are always linked to their callers and you may not want the GenServer to crash if the task crashes. Therefore, it is preferable to instead use Task.Supervisor.async_nolink/3 inside OTP behaviours. For completeness, here is an example of a GenServer that start tasks and handles their results:

With the server defined, you will want to start the task supervisor above and the GenServer in your supervision tree:

await_many(tasks, timeout \\ 5000)

This function receives a list of tasks and waits for their replies in the given time interval. It returns a list of the results, in the same order as the tasks supplied in the tasks input argument.

If any of the task processes dies, the current process will exit with the same reason as that task.

A timeout, in milliseconds or :infinity , can be given with a default value of 5000 . If the timeout is exceeded, then the current process will exit. Any task processes that are linked to the current process (which is the case when a task is started with async ) will also exit. Any task processes that are trapping exits or not linked to the current process will continue to run.

This function assumes the tasks' monitors are still active or the monitors' :DOWN message is in the message queue. If any tasks have been demonitored, or the message already received, this function will wait for the duration of the timeout.

This function can only be called once for any given task. If you want to be able to check multiple times if a long-running task has finished its computation, use yield_many/2 instead.

It is not recommended to await long-running tasks inside an OTP behaviour such as GenServer . See await/2 for more information.

child_spec(arg)

arg is passed as the argument to Task.start_link/1 in the :start field of the spec.

For more information, see the Supervisor module, the Supervisor.child_spec/2 function and the Supervisor.child_spec/0 type.

shutdown(task, shutdown \\ 5000)

Returns {:ok, reply} if the reply is received while shutting down the task, {:exit, reason} if the task died, otherwise nil .

The second argument is either a timeout or :brutal_kill . In case of a timeout, a :shutdown exit signal is sent to the task process and if it does not exit within the timeout, it is killed. With :brutal_kill the task is killed straight away. In case the task terminates abnormally (possibly killed by another process), this function will exit with the same reason.

It is not required to call this function when terminating the caller, unless exiting with reason :normal or if the task is trapping exits. If the caller is exiting with a reason other than :normal and the task is not trapping exits, the caller's exit signal will stop the task. The caller can exit with reason :shutdown to shut down all of its linked processes, including tasks, that are not trapping exits without generating any log messages.

If a task's monitor has already been demonitored or received and there is not a response waiting in the message queue this function will return {:exit, :noproc} as the result or exit reason can not be determined.

fun must be a zero-arity anonymous function.

This should only used when the task is used for side-effects (like I/O) and you have no interest on its results nor if it completes successfully.

If the current node is shutdown, the node will terminate even if the task was not completed. For this reason, we recommend to use Task.Supervisor.start_child/2 instead, which allows you to control the shutdown time via the :shutdown option.

start(module, function_name, args)

Start_link(fun).

This is used to start a statically supervised task under a supervision tree.

start_link(module, function, args)

Yield(task, timeout \\ 5000).

Returns {:ok, reply} if the reply is received, nil if no reply has arrived, or {:exit, reason} if the task has already exited. Keep in mind that normally a task failure also causes the process owning the task to exit. Therefore this function can return {:exit, reason} only if

A timeout, in milliseconds or :infinity , can be given with a default value of 5000 . If the time runs out before a message from the task is received, this function will return nil and the monitor will remain active. Therefore yield/2 can be called multiple times on the same task.

This function assumes the task's monitor is still active or the monitor's :DOWN message is in the message queue. If it has been demonitored or the message already received, this function will wait for the duration of the timeout awaiting the message.

If you intend to shut the task down if it has not responded within timeout milliseconds, you should chain this together with shutdown/1 , like so:

That ensures that if the task completes after the timeout but before shutdown/1 has been called, you will still get the result, since shutdown/1 is designed to handle this case and return the result.

yield_many(tasks, timeout \\ 5000)

This function receives a list of tasks and waits for their replies in the given time interval. It returns a list of two-element tuples, with the task as the first element and the yielded result as the second. The tasks in the returned list will be in the same order as the tasks supplied in the tasks input argument.

Similarly to yield/2 , each task's result will be

A timeout, in milliseconds or :infinity , can be given with a default value of 5000 .

Check yield/2 for more information.

Task.yield_many/2 allows developers to spawn multiple tasks and retrieve the results received in a given timeframe. If we combine it with Task.shutdown/2 , it allows us to gather those results and cancel the tasks that have not replied in time.

Let's see an example.

In the example above, we create tasks that sleep from 1 up to 10 seconds and return the number of seconds they slept for. If you execute the code all at once, you should see 1 up to 5 printed, as those were the tasks that have replied in the given time. All other tasks will have been shut down using the Task.shutdown/2 call.

what is task yield

What is the purpose behind Task.Yield() in the .NET client docs?

Alex D's profile photo

Luke Bakken

In  https://www.rabbitmq.com/dotnet-api-guide.html#consuming-async the example has the line "await Task.Yield();" once the message has been acked. From my understanding of .NET, this yield is not needed due to the entire call stack being async

what is task yield

Search Unity

what is task yield

A Unity ID allows you to buy and/or subscribe to Unity products and services, shop in the Asset Store and participate in the Unity community.

You are using an out of date browser. It may not display this or other websites correctly. You should upgrade or use an alternative browser .

Task.Yield() / Task.Delay(0) hang

Discussion in ' Experimental Scripting Previews ' started by Dejavu2017 , Feb 18, 2018 .

Dejavu2017

Code (CSharp): using System.Threading.Tasks ;   using UnityEngine ;   public class Test : MonoBehaviour {     void Start ( )     {         TestAsync ( ) ;     }       async void TestAsync ( )     {         int i = 0 ;         while ( true )         {             Debug . Log ( $ "### TestAsync: {++i}" ) ;             await Task . Delay ( 1 ) ; // ok             // await Task.Delay(0); // hang!             // await Task.Yield(); // hang!         }     } }   While posting this thread, I do notice that there is another thread talking about a similar issue: https://forum.unity.com/threads/help-with-async-await.470214/ But regardless, the above code when attached to a scene will hang the whole Unity editor when using Task.Delay(0) and Task.Yield(), is this the expected behavior?  

mkderoy

Unity Technologies

I've filed a bug for this, will update this post with the issuetracker asap https://issuetracker.unity3d.com/issues/task-dot-delay-0-hangs-with-async-await I've looked at this issue and found that Task.Delay(0) runs synchronously. You can find details about this behavior on stackoverflow https://stackoverflow.com/questions/36939006/not-using-await-task-delayx-x0-makes-ui-thread-freeze We have made one change due to this report however. Currently, when processing async/await continuations, we currently process the continuations that are spawned from existing continuations. This is why Task.Yield() would hang. Instead soon (once the changes land in 2018.1), if an "awaited" function calls await again, that continuation will be processed on to the next pass through the player loop. I should mention that we're also considering changes to where we process continuations in the playerloop (currently fixedupdate) since this behavior seems inconsistent in some ways with how other .NET implementations work.  

msfredb7

mkderoy said: ↑ I should mention that we're also considering changes to where we process continuations in the playerloop (currently fixedupdate) since this behavior seems inconsistent in some ways with how other .NET implementations work. Click to expand...

_geo__

I stumbled across this and did some digging (Unity 2019 LTS). It seems it moved to Update (evaluated once per frame). It is called between Update() and LateUpdate() and after coroutine "yield null". Here is how the updated lifecycle graph would look like: One caveat: they may be processed after the "Internal animation update", I have not tested that. Test was done with a loop like this: Code (CSharp): while ( predicate ( ) == false ) {     await Task . Yield ( ) ; } If you want to be extra sure it's done only once per frame you may use something like this: Code (CSharp):   // Thanks to https://github.com/modesttree/Unity3dAsyncAwaitUtil // License MIT: https://github.com/modesttree/Unity3dAsyncAwaitUtil/blob/master/License.md using System ; using System.Threading ; using System.Threading.Tasks ; using UnityEngine ;   public static class Awaiters {     readonly static WaitForUpdate _waitForUpdate = new WaitForUpdate ( ) ;       public static WaitForUpdate NextFrame     {         get { return _waitForUpdate ; }     }       public static async Task Until ( CancellationToken ct, Func < bool > predicate )     {         while ( predicate ( ) == false && ct . IsCancellationRequested == false )         {             await NextFrame ;         }     }       public static async Task While ( CancellationToken ct, Func < bool > predicate )     {         while ( predicate ( ) == true && ct . IsCancellationRequested == false )         {             await NextFrame ;         }     } } Would be nice to get some confirmation for this.  

Threeyes

I am facing the new bug in Unity2021.3.0f1: When I use await Task.Yield() in Editor, everything works fine, but after I build the game, it just 'freeze' on this line of code. Here's the test code: Code (CSharp):     void Start ( )     {         Test ( ) ;     }     async void Test ( )     {         Debug . LogError ( "Test Begin" ) ;         await Task . Yield ( ) ;         Debug . LogError ( "Test End" ) ; //Will not be executed!     }  

MiTschMR

Are you using IL2CPP? If yes, then it doesn’t work.  
MiTschMR said: ↑ Are you using IL2CPP? If yes, then it doesn’t work. Click to expand...

KAW0

For async/await use this: https://github.com/Cysharp/UniTask  

PooledAwait

Low allocation async/await for c#/.net.

Low-allocation utilies for writing async methods, and related tools

PooledValueTask / PooledValueTask<T>

Pooledtask / pooledtask<t>, fireandforget, configuredyieldawaitable, valuetaskcompletionsource<t>, pooledvaluetasksource / pooledvaluetasksource<t>, lazytaskcompletionsource / lazytaskcompletionsource<t>.

These are the main tools of the library; their purpose is to remove the boxing of the async state-machine and builder that happens when a method marked async performs an await on an awaitable target that is not yet complete , i.e.

If you’ve ever looked at an application that uses async / await in a memory profiler and seen things like System.Runtime.CompilerServices.AsyncTaskMethodBuilder 1.AsyncStateMachineBox 1 or YourLib.<<SomeMethod>g__Inner|8_0>d , then that’s what I’m talking about. You can avoid this by simply using a different return type:

For private / internal methods, you can probably just change the return type directly :

For methods on your public API surface, you can use a “local function” to achieve the same thing without changing the exposed return type:

(all of the Pooled* types have implicit conversion operators to their more well-recognized brethren).

And that’s it! That’s all you have to do. The “catch” (there’s always a catch) is that awaiting the same pending operation more than once no longer works :

In reality, this almost never happens . Usually you await something once , almost always right away. So… yeah.

These work very similarly to PooledValueTask[<T>] , but for the Task[<T>] API. It can’t be quite as frugal, as in most cases a Task[<T>] will still need to be allocated (unless it is the non-generic PooledTask signature, and the operation completes synchronously), but it still avoids the state-machine box etc. Note that this API is not impacted by the “you can only await it once” change (you can await these as many times as you like - they are, after all, Task[<T>] ), but again: this is used incredibly rarely anyway .

Ever find yourself needing a fire-and-forget API? This adds one. All you do is declare the return type as FireAndForget :

As soon as the method uses await against an incomplete operation, the calling task regains control as though it were complete; the rest of the operation continues in the background. The caller can simply await the fire-and-forget method with confidence that it only runs synchronously to the first incomplete operation. If you’re not in an async method, you can use “discard” to tell the compiler not to tell you to await it:

You won’t get unobserved-task-exception problems. If you want to see any exceptions that happen, there is an event FireAndForget.Exception that you can subscribe to. Otherwise, they just evaporate.

Related to FireAndForget - when you await Task.Yield() it always respects the sync-context/task-scheduler; sometimes you don’t want to . For many awaitables there is a .ConfigureAwait(continueOnCapturedContext: false) method that you can use to suppress this, but not on Task.Yield() … until now . Usage is, as you would expect:

Do you make use of TaskCompletionSource<T> ? Do you hate that this adds another allocation on top of the Task<T> that you actually wanted? ValueTaskCompletionSource<T> is your friend. It uses smoke and magic to work like TaskCompletionSource<T> , but without the extra allocation (unless it discovers that the magic isn’t working for your system). Usage:

The main difference here is that you now have a struct instead of a class . If you want to test whether an instance is a real value (as opposed to the default ), check .HasTask .

These again work like TaskCompletionSource<T> , but a: for ValueType[<T>] , and b: with the same zero-allocation features that PooledValueTask / PooledValueTask<T> exhibit. Once again, the “catch” is that you can only await their .Task once . Usage:

Sometimes, you have an API where you aren’t sure whether someone is subscribing to the Task / Task<T> results - for example you have properties like:

It would be a shame to allocate a Task for this just in case , so LazyTaskCompletionSource[<T>] allows you to rent state that can manage lazily creating a task. If the .Task is read before the value is set, a source is used to provide a pending task; if the result gets set before the value is read, then some optimizations may be possible ( Task.CompletedTask , etc). And if the .Task is never queried: no task or source is allocated. These types are disposable; disposing them releases any rented state for re-use.

Ever need a light-weight basic pool of objects? That’s this. Nothing fancy. The first API is a simple get/put:

Note that it leaves creation to you (hence the ?? new SomeType() ), and it is the caller’s responsibility to not retain and access a reference object that you have notionally returned to the pool.

Considerations:

A second API is exposed for use with value-types; there are a lot of scenarios in which you have some state that you need to expose to an API that takes object - especially with callbacks like WaitCallback , SendOrPostCallback , Action<object> , etc. The data will only be unboxed once at the receiver - so: rather than use a regular box, we can rent a box. Also, if you have multiple items of state that you need to convey - consider a value-tuple.

then later:

It is the caller’s responsibility to only access the state once.

The pool is global ( static ) and pretty modest in size. You can control it a bit by adding [PoolSize(...)] to the custom classes and value-types that you use.

IMAGES

  1. YIELD

    what is task yield

  2. c#

    what is task yield

  3. Flowchart to determine what kind of results a task will yield : Infographics

    what is task yield

  4. Yield Calculator

    what is task yield

  5. 2019-3-19-什么是task.yield

    what is task yield

  6. c#

    what is task yield

VIDEO

  1. COMPLETE PYRAMID FOR PRIZES AND HIGHER YIELD||@Mia Ordoña #solitairegame #online

  2. Time-Cost Tradeoff (1)

  3. LEC 13 Time-Cost Trade-off

  4. YIELD MANAGEMENT

  5. CENG186 L5 (Schedule Accelerate & Time-Cost Tradeoff)

  6. Time-Cost Tradeoff (2)

COMMENTS

  1. What Is a Task Environment?

    An organization’s task environment is the collection of factors that affects its ability to achieve goals. Common factors in the task environment include competitors, customers, suppliers and distributors.

  2. What Is Task Interdependence?

    Task interdependence sets rules and guidelines for the sharing of expertise, materials and information between members of an organization working on interdependent tasks.

  3. Who Makes Task Force Tools?

    Task Force is a house brand of home improvement store Lowes and is considered to be a budget brand of tools, similar in quality to Blue Hawk and beneath Kobalt. Lowes partnered with LG Sourcing Inc. to make Task Force tools.

  4. Task.Yield Method (System.Threading.Tasks)

    You can use await Task.Yield(); in an asynchronous method to force the method to complete asynchronously. If there is a current synchronization context

  5. When would I use Task.Yield()?

    With await Task.Yield() , you force it to be asynchronous in a way that the subsequent code is still run on the current context (just at a later point

  6. Task.Yield

    Below is the definition of the Task.Yield method. Creates an awaitable task that asynchronously yields back to the current context when awaited.

  7. yield()

    yield(). Suspends the current task and allows other tasks to execute. iOS 13.0+ iPadOS 13.0+ macOS 10.15+ Mac Catalyst 13.0+ tvOS 13.0+ watchOS 6.0+

  8. Task.Yield(...), Task.Delay(...)

    The book definition of this method is: “Creates an awaitable that asynchronously yields back to the current context when awaited”. Let see what

  9. Task Await vs Yield

    Hello, Fellow Alchemists! In today's video I will be looking at concurrent data processing, and explaining the difference between Async and

  10. A Tour of Task, Part 10: Promise Tasks

    Task.Yield has several interesting aspects. To begin with, it doesn't actually return a Task , so it's not really a Promise Task kind of

  11. Task

    Starts a task as part of a supervision tree with the given module , function , and args . yield(task, timeout \\ 5000). Temporarily blocks the current process

  12. What is the purpose behind Task.Yield() in the .NET client docs?

    Yield();" once the message has been acked. From my understanding of .NET, this yield is not needed due to the entire call stack being async. I

  13. Task.Yield() / Task.Delay(0) hang

    Tasks; using UnityEngine; public class Test : MonoBehaviour { void Start() { TestAsync(); } async... ... Task.Yield() / Task.Delay(0) hang.

  14. PooledAwait

    async ValueTask<int> SomeMethod() { await Task.Yield(); // *is not yet complete* return 42 }. If you've ever looked at an application that uses async