如何创建一个始终产生的任务?

omi*_*nug 3 .net c# multithreading asynchronous async-await

Task.Wait()or 相反Task.Result,await' Task在C#5中阻止执行等待的线程处于休眠状态.相反,使用await关键字的方法需要async使得调用await只是使方法返回表示async方法执行的新任务.

但是当await'ed Taskasync方法再次收到CPU时间之前完成时,await识别Task为已完成,因此该async方法将Task仅在稍后返回该对象.在某些情况下,这可能比可接受的晚,因为开发人员认为await'ing始终推迟其async方法中的后续语句可能是一个常见的错误.

错误async方法的结构可能如下所示:

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

    // perform a long operation
}
Run Code Online (Sandbox Code Playgroud)

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

我知道它应该像这样写:

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

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

... 或者那个:

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

    // perform a long operation
}
Run Code Online (Sandbox Code Playgroud)

但我没有找到最后两个模式漂亮,并希望防止错误发生.我正在开发一个提供的框架,getSthAsync第一个结构应该是通用的.所以getSthAsync应该返回一个Awaitable,它总是产生像YieldAwaitable返回的Task.Yield()那样.

不幸的是,任务并行库提供的大多数功能Task.WhenAll(IEnumerable<Task> tasks)只能在Tasks上运行,因此结果getSthAsync应该是a Task.

那么有可能返回一个Task总是收益的吗?

i3a*_*non 6

首先,异步方法的消费者不应该假设它会"屈服",因为它与异步无关.如果消费者需要确保卸载到另一个线程,他们应该使用它Task.Run来强制执行.

其次,我没有看到如何使用Task.Run或者Task.Yield有问题,因为它在异步方法中使用它返回a Task而不是a YieldAwaitable.

如果你想创建一个Task行为就像YieldAwaitable你可以Task.Yield在异步方法中使用:

async Task Yield()
{
    await Task.Yield();
}
Run Code Online (Sandbox Code Playgroud)

编辑:

正如评论中提到的那样,这种竞争条件可能并不总是会产生.这种竞争状态是固有如何TaskTaskAwaiter实现.要避免这种情况,你可以创建自己的TaskTaskAwaiter:

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();
}
Run Code Online (Sandbox Code Playgroud)

这将创建一个始终产生的任务,因为IsCompleted 始终返回false.它可以像这样使用:

public static readonly YieldTask YieldTask = new YieldTask();

private static async Task MainAsync()
{
    await YieldTask;
    // something
}
Run Code Online (Sandbox Code Playgroud)

注意:我强烈反对任何人实际做这种事情.

  • 或者只是`Task.Run(()=> {})`.这应该也有效. (2认同)