关于c#:一个任务可以有多个等待者吗? | 珊瑚贝

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)
         _callThisOnlyOnce = CallThisOnlyOnceAsync();

      return _callThisOnlyOnce;
 }

 private async Task CallThisOnlyOnceAsync()
 {
      PropagateSomeEvents();

      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()
{
  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 也可以做到这一点,但代码变得丑陋。

附言我的 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 && _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) 的结果存储在静态成员中,并在函数想要返回零时返回。

但是,我未能找到此代码示例。


来源:https://www.codenong.com/13530158/

微信公众号
手机浏览(小程序)

Warning: get_headers(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57

Warning: get_headers(): Failed to enable crypto in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57

Warning: get_headers(https://static.shanhubei.com/qrcode/qrcode_viewid_8722.jpg): failed to open stream: operation failed in /mydata/web/wwwshanhubei/web/wp-content/themes/shanhuke/single.php on line 57
0
分享到:
没有账号? 忘记密码?