.Net 異步方法加上「timeout」

在本羊讀大學的時候,Thread讓本羊雲裏霧裏,代碼寫的痛不欲生,真的是讓本羊腦殼裏不少「線」纏繞在一塊兒。多線程

以後,Task讓本羊代碼寫的飛起,甚至有時候根本不須要Task的時候仍是要寫上,那樣顯得檔次較高:多線程!充分利用CPU!niubility!異步

再以後,async/await語法糖橫空出世,更讓本羊欲罷不能!socket

 

然而,async

好東西吃多了總會膩的——吃貨ide

常在河邊走,哪有不溼鞋?——文藝青年性能

出來混老是要還的——「四道槓」騷年this

飆車一時爽,「全家」火葬場——老司機spa

 

扯遠了,反正大概意思就是,async/await 也會有不爽的時候。.net

好比: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:其實本羊的工做一直是單槍匹馬,意思就是公司裏只有本羊一個程序猿,因此,某領導是沒有的,其餘小夥伴也是沒有的,一切的一切都是本羊虛構的,包括大家。

相關文章
相關標籤/搜索