我正在为Windows 8项目提供异步服务,并且此服务有一些异步调用,一次只能调用一次。
public async Task CallThisOnlyOnce()
{
PropagateSomeEvents();
await SomeOtherMethod();
PropagateDifferentEvents();
}
由于你无法在锁定语句中封装异步调用,我想到使用AsyncLock
模式,但我想我也可以尝试这样的事情:
private Task _callThisOnlyOnce;
public Task CallThisOnlyOnce()
{
if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted)
_callThisOnlyOnce = null;
if(_callThisOnlyOnce == null)
_callThisOnlyOnce = CallThisOnlyOnceAsync();
return _callThisOnlyOnce;
}
private async Task CallThisOnlyOnceAsync()
{
PropagateSomeEvents();
await SomeOtherMethod();
PropagateDifferentEvents();
}
因此,你最终只能同时执行一次CallThisOnlyOnceAsync
调用,并且多个等待者都挂在同一个任务上。
这是一种“有效”的方法吗?或者这种方法有一些缺点吗?
答案 0 :(得分:7)
任务可以有多个等待者。但是,正如Damien指出的那样,你提出的代码存在严重的竞争条件。
如果您希望每次调用方法时执行代码(但不是同时执行),请使用AsyncLock
。如果您只想执行一次代码,请使用AsyncLazy
。
您提出的解决方案尝试组合多个调用,如果代码尚未运行,则再次执行代码。这更棘手,解决方案在很大程度上取决于您需要的确切语义。这是一个选项:
private AsyncLock mutex = new AsyncLock();
private Task executing;
public async Task CallThisOnlyOnceAsync()
{
Task action = null;
using (await mutex.LockAsync())
{
if (executing == null)
executing = DoCallThisOnlyOnceAsync();
action = executing;
}
await action;
}
private async Task DoCallThisOnlyOnceAsync()
{
PropagateSomeEvents();
await SomeOtherMethod();
PropagateDifferentEvents();
using (await mutex.LockAsync())
{
executing = null;
}
}
也可以使用Interlocked
执行此操作,但该代码很难看。
P.S。我的AsyncEx library中有AsyncLock
,AsyncLazy
和其他async
- 就绪原语。
答案 1 :(得分:4)
如果可能涉及多个线程,此代码看起来非常“活泼”。
一个例子(我确信还有更多)。假设_callThisOnlyOnce
目前是null
:
Thread 1 Thread 2
public Task CallThisOnlyOnce()
{
if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted)
_callThisOnlyOnce = null;
if(_callThisOnlyOnce == null)
public Task CallThisOnlyOnce()
{
if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted)
_callThisOnlyOnce = null;
if(_callThisOnlyOnce == null)
_callThisOnlyOnce = CallThisOnlyOnceAsync();
return _callThisOnlyOnce;
}
_callThisOnlyOnce = CallThisOnlyOnceAsync();
return _callThisOnlyOnce;
}
您现在可以同时运行2个来电。
对于多个等待者,是的,你可以这样做。我确定我已经看到MS的某些示例代码显示了一个优化,例如Task.FromResult(0)
的结果存储在一个静态成员中,并在函数想要返回零时返回。
但是,我找不到这个代码示例是不成功的。