2013-07-07 30 views
8

在任何時候,我都可以收到一個方法調用,需要長時間運行才能滿足。排隊異步代碼按順序執行

有幾種這些方法。因爲他們共享資源,所以它們不能同時運行很重要 - 每個應該按順序運行。

通常情況下,我想簡單地作出順序的電話:

var result1 = await Foo1Async(); 
var result2 = await Foo2Async(); 

然而,在這種情況下,方法給我打電話是異步:

void Bar1() 
{ 
    var result = await Foo1Async(); 
    // ... 
} 

void Bar2() 
{ 
    var result = await Foo2Async(); 
    // ... 
} 

我想我需要使用Task.ContinueWith,並想出了這一點:

readonly object syncLock = new Object(); 

private Task currentOperationTask = TaskUtilities.CompletedTask; 
public Task<TResult> EnqueueOperation<TResult>(Func<Task<TResult>> continuationFunction) 
{ 
    lock (this.syncLock) 
    { 
     var operation = this.currentOperationTask.ContinueWith(predecessor => continuationFunction()).Unwrap(); 
     this.currentOperationTask = operation; 
     return operation; 
    } 
} 

我用它是這樣的:

void Bar1() 
{ 
    var result = await EnqueueOperation(() => Foo1Async()); 
    // ... 
} 

void Bar2() 
{ 
    var result = await EnqueueOperation(() => Foo2Async()); 
    // ... 
} 

這是解決此問題的正確方法嗎?我應該注意哪些缺陷?

+0

,我不覺得這有什麼問題,但我想你可能會失去了'()=>'如果你改變你的簽名到'(任務 continuationFunction)'。 – SimpleVar

+0

@YoryeNathan這不會起作用,這意味着每個異步方法會立即開始執行,問題是如何避免這種情況。 – svick

+0

順便說一句,如果你的代碼看起來像這樣,你也可以直接寫'await EnqueueOperation(Foo1Async);'(不帶lambda)。 – svick

回答

1

您的代碼將正常工作,我認爲這是解決問題的合理方法。

更簡單的方法來做到這一點是使用AsyncLock from Nito AsyncEx

private readonly AsyncLock asyncLock = new AsyncLock(); 

public async Task<TResult> EnqueueOperation<TResult>(
    Func<Task<TResult>> continuationFunction) 
{ 
    using (await asyncLock.LockAsync()) 
    { 
     return await continuationFunction(); 
    } 
} 
+0

這是否也會等待與「EnqueueOperation」調用無關的任務? – SimpleVar

+0

@YoryeNathan不,它不會。但是如果你從UI線程調用'EnqueueOperation()',它也會在那裏調用'continuationFunction',這可能不是你想要的。 – svick