Can a Task have multiple awaiters?
我正在为 Windows 8 项目使用异步服务,并且该服务有一些异步调用,一次只能调用一次。
1
2 3 4 5 6 7 8 |
public async Task CallThisOnlyOnce()
{ PropagateSomeEvents(); await SomeOtherMethod(); PropagateDifferentEvents(); |
由于不能将异步调用封装在 lock 语句中,我想到了使用 AsyncLock 模式,但我想我不妨试试这样的:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
private Task _callThisOnlyOnce;
public Task CallThisOnlyOnce() { if(_callThisOnlyOnce != null && _callThisOnlyOnce.IsCompleted) _callThisOnlyOnce = null; if(_callThisOnlyOnce == null) return _callThisOnlyOnce; private async Task CallThisOnlyOnceAsync() await SomeOtherMethod(); PropagateDifferentEvents(); |
因此,您最终会得到调用 CallThisOnlyOnceAsync 仅同时执行一次,并且多个等待者会挂接到同一个任务上。
这是一种”有效”的方法吗?还是这种方法有一些缺点?
一个任务可以有多个等待者。但是,正如 Damien 指出的那样,您提出的代码存在严重的竞争条件。
如果您希望每次调用方法时都执行代码(但不是同时),请使用 AsyncLock。如果您希望代码只执行一次,请使用 AsyncLazy.
您提出的解决方案尝试组合多个调用,如果代码尚未运行,则再次执行该代码。这更棘手,解决方案在很大程度上取决于您需要的确切语义。这是一个选项:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
private AsyncLock mutex = new AsyncLock();
private Task executing; public async Task CallThisOnlyOnceAsync() await action; private async Task DoCallThisOnlyOnceAsync() await SomeOtherMethod(); PropagateDifferentEvents(); using (await mutex.LockAsync()) |
使用 Interlocked 也可以做到这一点,但代码变得丑陋。
附言我的 AsyncEx 库中有 AsyncLock、AsyncLazy 和其他 async 就绪的原语。
- 我喜欢这两个答案,但是由于您添加了实施建议,所以我选择了您的。此外,我尝试使用 Nuget 安装您的库,但在我的 Windows Store 项目中失败(无法解析 Microsoft.Bcl.Async)
- 尝试检查”包括预发布”复选框。我的包是预发布的(但 NuGet 没有正确检测到),并且 Microsoft.Bcl.Async 也是预发布的(NuGet 确实检测到了正确的)。
如果可能涉及多个线程,此代码看起来非常”活泼”。
一个例子(我相信还有更多)。假设 _callThisOnlyOnce 当前是 null:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Thread 1 Thread 2
public Task CallThisOnlyOnce() if(_callThisOnlyOnce == null) if(_callThisOnlyOnce == null) return _callThisOnlyOnce; return _callThisOnlyOnce; |
您现在有 2 个呼叫同时运行。
对于多个等待者,是的,您可以这样做。我确定我在某处看到过 MS 的示例代码,其中显示了优化,例如Task.FromResult(0) 的结果存储在静态成员中,并在函数想要返回零时返回。
但是,我未能找到此代码示例。
来源:https://www.codenong.com/13530158/