2015-08-22 53 views
3

與C#5中的Task.Wait()Task.Result,await'Task阻止執行等待的線程處於休止狀態。相反,使用await關鍵字的方法需要爲async,以便await的調用只是使方法返回表示執行方法的新任務。如何創建一個始終產生的任務?

但當await「編Task完成async方法已再次接收CPU時間之前,await識別Task作爲成品,因此async方法將只在稍後的時間返回Task對象。在某些情況下,這可能遲於可以接受的原因,因爲開發人員認爲的方法總是推遲在他的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,它總是像Task.Yield()返回的那樣返回YieldAwaitable。通過任務並行庫像Task.WhenAll(IEnumerable<Task> tasks)提供

不幸的是大多數功能只在Task s運行所以getSthAsync的結果應該是一個Task

那麼有可能返回一個總是會產生的Task

回答

6

首先,異步方法的使用者不應該認爲它會「屈服」,因爲它與異步無關。如果消費者需要確保有卸載到另一個線程,他們應該使用Task.Run來強制執行。

其次重要的是,我沒有看到如何使用Task.Run,或者因爲它返回一個Task而不是YieldAwaitable異步方法內部使用Task.Yield是有問題的。

如果你想創建一個Task表現得像YieldAwaitable你可以使用Task.Yield異步方法中:

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

編輯:

正如在評論中提到的,這有一個競爭條件它可能並不總是屈服。這種競爭條件是如何實現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(); 
} 

這將創建一個總是產量,因爲IsCompleted總是返回false的任務。它可以這樣使用:

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

相關問題