前文傳送門 dotNet開發基礎系列
C#異步編程基礎入門總結
C#泛型入門學習泛型類、泛型集合、泛型方法、泛型約束、泛型委託
C#異常處理總結
webapi token、參數簽名是如何生成的
C#擴展(2):Random的擴展
程序員:我終於知道post和get的區別
Try-Catch無法正確定位異常位置,我推薦2個有效技巧
之前在實際工作中,遇到過這樣的問題,異步獲取GPS定位信息。一個實際的問題出現了,在第一次定位的時候一般時間都比較長,現在的要做的是,當超出一定的時間,就提醒用戶。
Task.ContinueWith
可以實現異步等待任務完成
Task.Wait
可以實現同步等待完成任務後並超時,此方法會阻塞的線程,就是“卡界面”
那麼如何實現異步等待任務並在超時時進行一定的處理呢?
Task的實例方法 Wait
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken);
public bool Wait(int millisecondsTimeout);
public bool Wait(TimeSpan timeout);
public void Wait();
public void Wait(CancellationToken cancellationToken);
Wait方法所支持的的特點:一個是取消任務,一個是超時。但是不可避免的是阻塞線程,一般在主線程中(UI線程)是要避免耗時任務的。如果真要去等待獲取定位信息的Task,會不可避免地卡住界面,用戶體驗不夠良好。
Task的靜態方法
Task.When** 可等待多個異步任務,不阻塞線程,所以可以利用Delay靜態方法“間接”實現異步超時的處理,非阻塞的方式!
var resultTask = Task.WhenAny(task, Task.Delay(timeout))
我們再WhenAny 方法中代入兩個task參數:源task、新建的延遲的task(ps:即超時的時間task),再得到resultTask。
//
// 摘要:
// 任何提供的任務已完成時,創建將完成的任務。
//
// 參數:
// tasks:
// 等待完成的任務。
//
// 返回結果:
// 表示提供的任務之一已完成的任務。 返回任務的結果是完成的任務。
public static Task<Task> WhenAny(params Task[] tasks);
異步Task 實現超時處理的方法
Task.WhenAny(task, Task.Delay(timeout)),再比較這兩個Task的執行先後。實現代碼如下:
public static async Task<TResult> WaitAsync<TResult>(Task<TResult> task, TimeSpan timeout)
{
if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
{
//指定時間內完成的處理
return await task;
}
else
{
//超時處理
throw new TimeoutException("The operation has timed out.");
}
}
思考
上述方法所實現的等待一個獲取gps的耗時任務,是沒有任何問題的。並不會產生性能、卡界面等問題,但是如果在源task(ps:執行獲取gps的task),在超時到期之前完成,則不會取消在Task.Delay調用中啓動的內部計時器作業。
當調用多次該方法時,“殭屍”計時器作業的數量變得越來越大時。性能可能會受到影響
參考鏈接
https://devblogs.microsoft.com/pfxteam/crafting-a-task-timeoutafter-method/
https://stackoverflow.com/questions/4238345/asynchronously-wait-for-taskt-to-complete-with-timeout