2016-10-11 40 views
2

我意识到这不是取消任务的第一个线程,但我没有找到适合我需求的任何合适的东西。C#UWP取消任务+信令子

这里的情况:

  • 我有一个模块化的系统,其中模块由控制器类进行初始化,动态根据配置。
  • 每个模块实现Connect()方法。该方法本身是黑匣子,但对于背景信息:某些模块尝试创建蓝牙连接。
  • 如果连接时间过长,我想取消尝试并在5分钟内重试。

起初,我把重试逻辑放在模块实现中。这很好,但在模块上重复使用,并不属于模块的核心职责。于是我考虑把逻辑放在控制器中,但我努力寻找一个很好的实现。

这里的问题是我不仅想要取消任务,我想以一种很好的方式来完成任务。如果有物体需要关闭/处置,模块应该可以这样做。此外,该模块应该能够将其状态设置为“断开连接”而不是“连接”(并且我宁愿让模块执行该操作,也不要使用公共方法,以便外部更改状态)。当连接和超时逻辑位于模块内部时,这很容易,但引入外部超时变得更具挑战性。

这里是我可以预见,当一个模块没有及时响应的流程:

  • 控制器创建一个新的模块实例
  • 控制器,以连接模块
  • 控制器还初始化调用connect()一个定时器监视模块在30秒内完成连接
  • 计时器关闭,模块尚未完成工作尚未
  • Connect()方法应该被通知取消最好通过在可以捕获和处理的Connect方法内抛出异常。
  • 异常处理程序可以清理干净的东西,方法返回。

上面的流程并不存在,据我所知。取消令牌的工作方式不同,并要求我进行事件或轮询。这很好,但是我回过头来将这个逻辑包含到我的模块中,为什么不直接在那里做整个重试。所以我想知道是否有任何甜美的图案可以让我这样做。

我正在为Windows 10 UWP构建。我没有特意加入任何代码,因为我现在拥有的东西无论如何都是垃圾。

回答

1

不要让CancellationToken出现什么问题,您的异步操作支持取消,或者没有办法停止正在执行的代码,除非您终止线程或进程。 这是一个超时示例。

public static async Task Run() 
      { 
       var module = new Module(); 

       //No timeout 
       await module.Connect(1, CancelAfter(2000)); 

       try 
       { 
        // Timeout 
        await module.Connect(5, CancelAfter(1000)); 
       } 
       catch (Exception) 
       { 

        module.Dispose(); 
       } 

      } 

      public static CancellationToken CancelAfter(int millisecondsDelay) 
      { 
       var token = new CancellationTokenSource(); 
       token.CancelAfter(millisecondsDelay); 
       return token.Token; 
      } 

      public class Module : IDisposable 
      { 
       public async Task Connect(int count, CancellationToken cancel) 
       { 
        for (int i = 0; i < count; i++) 
        { 
         //This is just to simulte some work Task.Delay can be canceled as well with Task.Delay(500,cancel) 
         await Task.Delay(500); 
         cancel.ThrowIfCancellationRequested(); 
        } 
       } 

       public void Dispose() 
       { 

       } 
      } 

可以超时任何任务与

public static class TaskTimeout 
    { 
     public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) 
     { 
      if (task != await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
      { throw new TimeoutException(); } 
     } 

     public static async Task<T> TimeoutAfter<T>(this Task<T> task, int millisecondsTimeout) 
     { 
      if (task != await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
      { throw new TimeoutException(); } 
      else { return task.Result; } 
     } 
    } 

但这不会取消你的连接只有当连接是取消它可以取消。大多数.net中的Async调用都实现了CancellationToken,因此如果您的连接使用了它。

+0

这正是问题所在。 My Connect()没有for或while循环,我可以一次又一次地检查令牌。它会触发连接呼叫,并且会一直等到有连接。这是我需要以某种方式杀死的那个呼叫,目前我通过从计时器事件中处置对象来完成。如果我们可以在任何给定的时间点触发Connect()方法中的运行时异常,那将会更好。 – Jasper

+0

我做了一个编辑,告诉你如何超时任何任务,但你的配置需要处理处置。 –