如何创建一个总是产生收益的任务? [英] How to create a Task which always yields?

查看:88
本文介绍了如何创建一个总是产生收益的任务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Task.Wait() Task.Result 相比, await 在C#5中执行 Task 可以防止执行等待的线程处于休止状态。相反,使用 await 关键字的方法需要 async ,以便调用 await 只是使该方法返回一个新任务,该任务表示执行 async 方法。

In contrast to Task.Wait() or Task.Result, await’ing a Task in C# 5 prevents the thread which executes the wait from lying fallow. Instead, the method using the await keyword needs to be async so that the call of await just makes the method to return a new task which represents the execution of the async method.

但是,当等待编辑了任务时,会在 async 方法再次获得了CPU时间,等待任务识别为已完成,因此将 async 方法将仅在以后返回 Task 对象。在某些情况下,这可能会晚于可接受的时间,因为这可能是一个常见错误,即开发人员认为 await 总是会延迟其中的后续语句异步方法。

But when the await’ed Task completes before the async method has received CPU time again, the await recognizes the Task as finished and thus the async method will return the Task object only at a later time. In some cases this would be later than acceptable because it probably is a common mistake that a developer assumes the await’ing always defers the subsequent statements in his async method.

错误的异步方法的结构如下所示:

The mistaken async method’s structure could look like the following:

async Task doSthAsync()
{
    var a = await getSthAsync();

    // perform a long operation
}

然后有时 doSthAsync()仅在很长一段时间后才会返回 Task

Then sometimes doSthAsync() will return the Task only after a long time.

我知道应该这样写:

async Task doSthAsync()
{
    var a = await getSthAsync();

    await Task.Run(() =>
    {
        // perform a long operation
    };
}

...或那样:

async Task doSthAsync()
{
    var a = await getSthAsync();
    await Task.Yield();

    // perform a long operation
}

但是我找不到最后两种模式,并且想要防止错误我正在开发一个提供 getSthAsync 的框架,并且第一个结构应该是通用的,因此 getSthAsync 应该返回一个Awaitable总是像 Task.Yield()返回的 YieldAwaitable 一样。

But I do not find the last two patterns pretty and want to prevent the mistake to occur. I am developing a framework which provides getSthAsync and the first structure shall be common. So getSthAsync should return an Awaitable which always yields like the YieldAwaitable returned by Task.Yield() does.

不幸的是,任务并行库提供的大多数功能,例如 Task.WhenAll(IEnumerable< Task> task)仅在 Task上运行 s,所以 getSthAsync 的结果应该是 Task

Unfortunately most features provided by the Task Parallel Library like Task.WhenAll(IEnumerable<Task> tasks) only operate on Tasks so the result of getSthAsync should be a Task.

是吗可能返回总是产生收益的 Task

So is it possible to return a Task which always yields?

推荐答案

首先,异步方法的使用者不应假定它将屈服,因为这与异步无关。如果使用者需要确保另一个线程可以卸载,则应使用 Task.Run 来强制执行。

First of all, the consumer of an async method shouldn't assume it will "yield" as that's nothing to do with it being async. If the consumer needs to make sure there's an offload to another thread they should use Task.Run to enforce that.

第二,我不知道使用 Task.Run Task.Yield 有什么问题,因为在异步方法内部使用,该方法返回 Task 而不是 YieldAwaitable

Second of all, I don't see how using Task.Run, or Task.Yield is problematic as it's used inside an async method which returns a Task and not a YieldAwaitable.

如果您要创建行为类似于 YieldAwaitable Task ,则只需使用 Task.Yield 在异步方法中:

If you want to create a Task that behaves like YieldAwaitable you can just use Task.Yield inside an async method:

async Task Yield()
{
    await Task.Yield();
}

编辑:

正如评论中提到的那样,这是一种竞争条件,可能并不总是让步。此竞争条件与 Task TaskAwaiter 的实现方式有关。为避免这种情况,您可以创建自己的 Task TaskAwaiter

As was mentioned in the comments, this has a race condition where it may not always yield. This race condition is inherent with how Task and TaskAwaiter are implemented. To avoid that you can create your own Task and TaskAwaiter:

public class YieldTask : Task
{
    public YieldTask() : base(() => {})
    {
        Start(TaskScheduler.Default);
    }

    public new TaskAwaiterWrapper GetAwaiter() => new TaskAwaiterWrapper(base.GetAwaiter());
}

public struct TaskAwaiterWrapper : INotifyCompletion
{
    private TaskAwaiter _taskAwaiter;

    public TaskAwaiterWrapper(TaskAwaiter taskAwaiter)
    {
        _taskAwaiter = taskAwaiter;
    }

    public bool IsCompleted => false;
    public void OnCompleted(Action continuation) => _taskAwaiter.OnCompleted(continuation);
    public void GetResult() => _taskAwaiter.GetResult();
}

这将创建一个始终产生的任务,因为已完成 总是返回false。可以这样使用:

This will create a task that always yields because IsCompleted always returns false. It can be used like this:

public static readonly YieldTask YieldTask = new YieldTask();

private static async Task MainAsync()
{
    await YieldTask;
    // something
}

注意:我强烈劝阻任何人不要实际做这种事情。

Note: I highly discourage anyone from actually doing this kind of thing.

这篇关于如何创建一个总是产生收益的任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆