云点博客-关注全栈开发云点博客

关注全栈开发
提升用户体验

.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]不能设置?

 

本文到此结束,各位新老司机,点个赞吧!

 

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


赞(0) 打赏