2013-07-07 29 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