2015-08-22 2 views
3

В отличие от Task.Wait() или Task.Result, await 'ing a Task в C# 5 предотвращает поток, который выполняет ожидание от лежачего пара. Вместо этого метод, использующий ключевое слово await, должен быть async, так что вызов await просто заставляет метод возвращать новую задачу, которая представляет выполнение метода async.Как создать задачу, которая всегда дает?

Но когда await «эд Task завершается до метода async получил процессорное время снова, await признает Task, как законченную и, таким образом, метод async возвращает Task объект только в более позднее время. В некоторых случаях это будет позже, чем приемлемо, потому что, вероятно, это распространенная ошибка, которую разработчик предполагает, что оператор await всегда откладывает последующие утверждения в своем методе async.

структура ошибочного async методы может выглядеть следующим образом:

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

    // perform a long operation 
} 

Тогда иногда doSthAsync() возвратит Task только после долгого времени.

Я знаю, что это должно скорее быть написано так:

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, который всегда дает как YieldAwaitable, возвращенный Task.Yield().

К сожалению, большинство возможностей, обеспечиваемых библиотеки Task Parallel Library, как Task.WhenAll(IEnumerable<Task> tasks) работают только на Task с таким результатом getSthAsync должен быть Task.

Как можно вернуть Task, который всегда дает?

ответ

6

Прежде всего, потребитель асинхронного метода не должен предполагать, что он «уступит», поскольку это не имеет никакого отношения к тому, что он является асинхронным. Если потребителю необходимо убедиться, что есть разгрузка в другой поток, он должен использовать Task.Run для обеспечения этого.

Во-вторых, я не вижу, как с помощью Task.Run или Task.Yield является проблематичным, так как он используется в качестве метода асинхронной который возвращает Task и не YieldAwaitable.

Если вы хотите создать Task, который ведет себя как YieldAwaitable вы можете просто использовать Task.Yield внутри метода асинхронной:

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

Edit:

Как было упомянуто в комментариях, это имеет состояние гонки где он может не всегда уступать.Это условие гонки связано с тем, как реализованы Task и TaskAwaiter. Чтобы избежать этого, вы можете создать свой собственный Task и 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(); 
} 

Это создаст задачу, которая всегда урожаи, потому что IsCompletedвсегда возвращает ложное. Его можно использовать следующим образом:

public static readonly YieldTask YieldTask = new YieldTask(); 

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

Примечание: Я сильно отговариваю кого-либо от фактического выполнения такого рода вещей.

+0

Использование не является проблематичным; проблема в том, что это необходимо сделать (на мой взгляд). – ominug

+2

Или просто 'Task.Run (() => {})'. Это тоже должно работать. – usr

+0

@usr true, только у него есть условие гонки, так как задача может завершиться до проверки «IsCompleted». – i3arnon

Смежные вопросы