• .Net 异步方法加上“timeout”


    在本羊读大学的时候,Thread让本羊云里雾里,代码写的痛不欲生,真的是让本羊脑袋里很多“线”缠绕在一起。

    之后,Task让本羊代码写的飞起,甚至有时候根本不需要Task的时候还是要写上,那样显得档次较高:多线程!充分利用CPU!niubility!

    再之后,async/await语法糖横空出世,更让本羊欲罢不能!

     

    然而,

    好东西吃多了总会腻的——吃货

    常在河边走,哪有不湿鞋?——文艺青年

    出来混总是要还的——“四道杠”骚年

    飙车一时爽,“全家”火葬场——老司机

     

    扯远了,反正大概意思就是,async/await 也会有不爽的时候。

    比如:UdpClient.ReceiveAsync()

     

    某领导:只等待5秒,过时不候。这样比较节约性能。

    ——哎哟,很有道理的样子,那就改改?

     

    于是,本羊很快就找到了一个属性:UdpClient.Client.ReceiveTimeout

     

    UdpClient.Client.ReceiveTimeout=5000;//领导说了只等5秒 <--注释在这里

    var r=UdpClient.ReceiveAsync();

    ……(省略其他代码)

     

    看到没,本羊还很善意的给代码加上了“注释”,方便其他小伙伴理解本羊的优雅的高深的简洁的代码。

    然后拿起水杯,小小的抿了一口,伸出右手食指,轻轻的按下“F5”,左手在桌子上很有节奏的敲了5下,哎哟,啥情况?

    再敲5下,再敲5下,再敲5下……

    好吧,本羊输了。

    是时候打开MSDN了,原来,ReceiveTimeout这玩意儿只对同步方法有效,ReceiveAsync根本不管这Y的。

     

    再研究研究?

     

    经过一番刻苦钻研,新的代码来了:

    var t=new CancellationTokenSource();//这玩意儿就是用来配合Task,做取消功能的

    Task.Delay(5000, t.Token).ContinueWith(task =>
    {
         if (!task.IsCanceled && task.IsCompleted)//不是被取消而且已经完成
             {
                 client.Close();//释放UdpClient
              }
    });//咱走着瞧(不会阻塞当前线程),5秒之后再来

    try

    {

      var r=await client.ReceiveAsync();

      return r;//返回结果

    }

    catch

    {

      throw new TimeoutException("过时不候!");//优雅的抛出错误提示

    }

     

    以上代码使用了很长一段时间,直到今天本羊又看到一些关于Task的文章,原来还有更优雅的实现方式!

    不多说,上代码:

    var t=new CancellationTokenSource();//又是这玩意儿

    var r=await Task.WhenAny(client.ReceiveAsync(), Task.Delay(5000, t.Token)) as Task<UdpReceiveResult>;//还是5秒,过时不候

    if(r!=null)//如果是Delay先返回,是不能 as Task<UdpReceiveResult>的,r=null。
    {

      t.Cancel();//取消那个Delay,其实也可以不用处理,反正5秒后那家伙就自己去西天了

      return r.Result;

    }

    else

    {

      client.Close();//释放UdpClient,不然还是在ReceiveAsync

      throw new TimeoutException("过时不候!");

    }

     

    再来一个:

    var tasks=new Task[]{client.ReceiveAsync()};

    var index=awati Task.Run(()=>

    {

      return Task.WaitAny(tasks, 5000);//返回完成的Task在集合中的序号

    });//Wait[xxx]会阻塞线程,所以用一个Run包裹住

    if(index==0)

    {

      return (tasks[0] as Task<UdpReceiveResult>).Result;

    }

    else

    {

      client.Close();

      throw new TimeoutException("过时不候!");

    }

    又或者(重磅推荐):

    var t = client.ReceiveAsync();
    if (await Task.Run(() => { return t.Wait(5000); }))
    {
      return t.Result;
    }
    else
    {
      client.Close();
      throw new TimeoutException("过时不候!");
    }

    CancellationTokenSource、Task.Delay(delay)都用不到了,爽歪歪~~

     

    Wait[xxx]有多个重载,可以设置timeout、CancellationToken,不知道为啥When[xxx]不能设置?

     

    最后放上Task的拓展方法:

     1 public static class TaskExtension
     2     {
     3         /// <summary>
     4         /// 异步等待
     5         /// 任务完成立即返回,否则在指定时间后抛出TimeoutException
     6         /// </summary>
     7         /// <param name="task"></param>
     8         /// <param name="timeout">等待时间(毫秒)</param>
     9         /// <returns></returns>
    10         public static async Task WaitAsync(this Task task, int timeout)
    11         {
    12             if (!await Task.Run(() => { return task.Wait(timeout); }))
    13                 throw new TimeoutException();
    14         }
    15 
    16         /// <summary>
    17         /// 异步等待
    18         /// 任务完成立即返回,否则在指定时间后抛出TimeoutException
    19         /// </summary>
    20         /// <param name="task"></param>
    21         /// <param name="timeout">等待时间(毫秒)</param>
    22         /// <returns></returns>
    23         public static async Task<T> WaitAsync<T>(this Task<T> task, int timeout)
    24         {
    25             if (await Task.Run(() => { return task.Wait(timeout); }))
    26                 return task.Result;
    27             throw new TimeoutException();
    28         }
    29 
    30         /// <summary>
    31         /// 给“无法取消”的任务传入CancellationToken
    32         /// </summary>
    33         /// <param name="task">无法传入CancellationToken的任务</param>
    34         /// <param name="token">CancellationToken</param>
    35         /// <returns></returns>
    36         public static async Task WaitAsync(this Task task, CancellationToken token)
    37         {
    38             if (!await Task.Run(() => { try { return task.Wait(-1, token); } catch { return false; } }))
    39                 throw new TaskCanceledException();
    40         }
    41 
    42         /// <summary>
    43         /// 给“无法取消”的任务传入CancellationToken
    44         /// </summary>
    45         /// <typeparam name="T">返回类型</typeparam>
    46         /// <param name="task">无法传入CancellationToken的任务</param>
    47         /// <param name="token">CancellationToken</param>
    48         /// <returns></returns>
    49         public static async Task<T> WaitAsync<T>(this Task<T> task, CancellationToken token)
    50         {
    51             if (await Task.Run(() => { try { return task.Wait(-1, token); } catch { return false; } }))
    52                 return task.Result;
    53             throw new TaskCanceledException();
    54         }
    55 
    56         /// <summary>
    57         /// 封装“无法取消”的任务
    58         /// </summary>
    59         /// <typeparam name="T">返回类型</typeparam>
    60         /// <param name="task">无法传入CancellationToken的任务</param>
    61         /// <param name="watcher">可取消的无限等待任务[Task.Delay(-1,CancellationToken)]</param>
    62         /// <returns></returns>
    63         public static async Task<T> WaitAsync<T>(this Task<T> task, Task watcher)
    64         {
    65             var r = await Task.WhenAny(task, watcher);
    66             if (r == task)
    67             {
    68                 return task.Result;
    69             }
    70             throw new TaskCanceledException();
    71         }
    72 
    73 
    74         public static CancellationTokenRegistration RegisterWidth<T>(this CancellationToken token, T state, Action<T> action)
    75         {
    76             return token.Register((p) => { action((T)p); }, state);
    77         }
    78     }
    View Code

     

    PS:其实本羊的工作一直是单枪匹马,意思就是公司里只有本羊一个程序猿,所以,某领导是没有的,其他小伙伴也是没有的,一切的一切都是本羊虚构的,包括你们。

  • 相关阅读:
    JavaScript你所不知道的困惑(3)
    Android的代码都得自己一个个敲一遍吗?
    现代化农业在美国的兴起与发展
    高内聚与低耦合实现小记
    iOS 载入图片选择imageNamed 方法还是 imageWithContentsOfFile?
    swift笔记——环境搭建及Hello,Swift!
    HDU 4832(DP+计数问题)
    [TJOI2019]甲苯先生的线段树
    2019-8-31-C#-如何写-DEBUG-输出
    2019-8-31-C#-如何写-DEBUG-输出
  • 原文地址:https://www.cnblogs.com/LCHL/p/5549856.html
Copyright © 2020-2023  润新知