【问题标题】:How to combine time base polling with awaitable Task如何将时基轮询与等待任务结合起来
【发布时间】:2016-09-23 18:00:30
【问题描述】:

我已经实现了一个基于 Timer 的 polling-worker。例如,您可以在客户端考虑TryConnect——我调用TryConnect,它最终会在一段时间内连接。它处理多个线程,如果连接已经在进程中,所有后续TryConnect 立即返回,无需任何额外操作。在内部,我只是创建一个计时器,并每隔一段时间尝试连接——如果连接失败,我会再试一次。以此类推。

小缺点是它是“fire&forget”模式,现在我想将它与“async/await”模式结合起来,即改为调用:

client.TryConnect(); // returns immediately
// cannot tell if I am connected at this point

我想这样称呼它:

await client.TryConnect();
// I am connected for sure

如何更改我的实现以支持“async/await”?我正在考虑创建空的Task(仅用于await),然后用FromResult 完成它,但是这个方法创建一个新任务,它没有完成给定的实例。

为了记录,当前的实现看起来像这样(只是代码的草图):

public void TryConnect()
{
   if (this.timer!=null)
   {
     this.timer = new Timer(_ => tryConnect(),null,-1,-1);
     this.timer.Change(0,-1); 
   }
}
private void tryConnect()
{
   if (/*connection failed*/)
     this.timer.Change(interval,-1);
   else
     this.timer = null;
}

【问题讨论】:

    标签: c# timer async-await polling


    【解决方案1】:

    缺少一个好的Minimal, Complete, and Verifiable code example 是不可能提供任何具体建议的。鉴于您所写的内容,可能您正在寻找的是TaskCompletionSource。例如:

    private TaskCompletionSource<bool> _tcs;
    
    public async Task TryConnect()
    {
       if (/* no connection exists */)
       {
         if (_tcs == null)
         {
           this.timer = new Timer(_ => tryConnect(),null,-1,-1);
           this.timer.Change(0,-1); 
           _tcs = new TaskCompletionSource<bool>();
         }
    
         await _tcs.Task;
       }
    }
    
    private void tryConnect()
    {
       if (/*connection failed*/)
         this.timer.Change(interval,-1);
       else
       {
         _tcs.SetResult(true);
         _tcs = null;
         this.timer = null;
       }
    }
    

    注意事项:

    • 如果在建立连接后再次调用TryConnect(),您的原始代码示例将重试连接逻辑。我希望您真正想要的是检查是否存在有效连接,因此我稍微修改了上面的内容以进行检查。如果您确实总是想尝试新的连接,当然可以删除该部分,即使该连接已经存在。
    • 代码在设置结果后立即将_tcs 设置为null。请注意,任何等待或以其他方式存储了 _tcs 对象的 Task 值的代码都将隐式引用当前的 _tcs 对象,因此在这里丢弃该字段的引用不是问题。
    • 没有非泛型TaskCompletionSource。因此,对于只需要Task 的场景,您可以将泛型类型与占位符类型一起使用,例如我在这里所做的boolobject 或其他。我可以像SetResult(true) 一样调用SetResult(false),这在这个例子中并不重要。重要的是 Task 已完成,而不是返回什么值。
    • 上面使用async关键字使TryConnect()成为异步方法。恕我直言,这更具可读性,但当然会在额外的Task 中产生轻微的开销来表示方法的操作。如果您愿意,也可以不使用 async 方法直接执行相同的操作:
    public Task TryConnect()
    {
       if (/* no connection exists */)
       {
         if (_tcs == null)
         {
           this.timer = new Timer(_ => tryConnect(),null,-1,-1);
           this.timer.Change(0,-1); 
           _tcs = new TaskCompletionSource<bool>();
         }
    
         return _tcs.Task;
       }
    
       return Task.CompletedTask;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-14
      • 1970-01-01
      • 1970-01-01
      • 2021-07-22
      • 1970-01-01
      • 2016-02-09
      • 1970-01-01
      相关资源
      最近更新 更多