### 任務超時取消 示例 ``` public static async Task TimeoutCancelTask() { CancellationTokenSource cts = new CancellationTokenSource();//取消令牌 Task task = DoActi ...
任務超時取消 示例
public static async Task TimeoutCancelTask()
{
CancellationTokenSource cts = new CancellationTokenSource();//取消令牌
Task task = DoAction(cts);//業務非同步任務
double timeoutSeconds = 2;//超時時間 秒
Task delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));//指定一個等待任務 等待到超時時間
Task completeTask = await Task.WhenAny(task, delayTask);//等待兩個任務,任意一個任務執行完成。返回率先完成的任務
if (completeTask == delayTask)//如果率先完成的是超時等待任務,就說明業務任務執行超時了。
{
cts.Cancel();//取消令牌 狀態改為取消
Console.WriteLine("任務已超時取消");
}
else
{
Console.WriteLine("任務已完成");
}
}
//模擬業務任務
public static async Task DoAction(CancellationTokenSource cts)
{
await Task.Delay(200);
for (int i = 1; i <= 5; i++)
{
if (cts.IsCancellationRequested)//在業務任務每個耗時的操作開始之前判斷取消令牌是否已取消
break;
Console.WriteLine(i);
await Task.Delay(TimeSpan.FromSeconds(1));//模擬業務操作,耗時任務。
}
}
封裝幫助方法
public static class TaskExtensions
{
/// <summary>
/// 任務超時取消
/// </summary>
/// <param name="func">業務任務(超時要取消任務的話 需要在耗時操作之前 判斷cts如果取消就結束方法)</param>
/// <param name="timeoutSeconds">超時時間 秒</param>
/// <param name="cts">任務取消令牌</param>
/// <returns>true執行成功 false超時取消</returns>
public static async Task<bool> TimeoutCancelAsync(
Func<CancellationTokenSource, Task> func, double timeoutSeconds, CancellationTokenSource cts)
{
Task task = func.Invoke(cts);
Task delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), cts.Token);
Task completeTask = await Task.WhenAny(task, delayTask);
if (completeTask == task)
return true;
cts.Cancel();
Console.WriteLine("【TimeoutCancelAsync】任務執行超時已取消。");
return false;
}
/// <summary>
/// 任務超時取消 (帶泛型返回值)
/// </summary>
/// <param name="func">業務任務帶返回值(超時要取消任務的話 需要在耗時操作之前 判斷cts如果取消就結束方法)</param>
/// <param name="timeoutSeconds">超時時間 秒</param>
/// <param name="cts">任務取消令牌</param>
/// <returns>IsSuccess:true執行成功 false超時取消 Result:任務執行成功的結果</returns>
public static async Task<(bool IsSuccess, T Result)> TimeoutCancelAsync<T>(
Func<CancellationTokenSource, Task<T>> func, double timeoutSeconds, CancellationTokenSource cts)
{
Task<T> task = func.Invoke(cts);
Task<T> delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), cts.Token)
.ContinueWith(_ => default(T));
Task completeTask = await Task.WhenAny<T>(task, delayTask);
if (completeTask == task)
return (true, task.Result);
cts.Cancel();
Console.WriteLine("【TimeoutCancelAsync】任務執行超時已取消。");
return (false, delayTask.Result);
}
/// <summary>
/// 任務超時取消 然後重新執行
/// </summary>
/// <param name="func">業務任務(超時要取消任務的話 需要在耗時操作之前 判斷cts如果取消就結束方法)</param>
/// <param name="timeoutSeconds">超時時間 秒</param>
/// <param name="maxRetryCount">最大重試次數</param>
/// <param name="cts">任務取消令牌</param>
/// <returns>是否成功</returns>
public static async Task<bool> TimeoutRetryAsync(
Func<CancellationTokenSource, Task> func, double timeoutSeconds, int maxRetryCount, CancellationTokenSource cts)
{
for (int i = 0; i <= maxRetryCount; i++)
{
if (cts.IsCancellationRequested)
break;
if (i > 0)
Console.WriteLine($"【TimeoutRetryAsync】任務第{i}次重試開始...");
CancellationTokenSource currentCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
Task task = func.Invoke(currentCts);
Task delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), currentCts.Token);
Task completeTask = await Task.WhenAny(task, delayTask);
if (completeTask == task)
{
currentCts.Dispose();
return true;
}
currentCts.Cancel();
Console.WriteLine("【TimeoutRetryAsync】任務執行超時已取消。");
}
return false;
}
/// <summary>
/// 任務超時取消 然後重新執行 (帶泛型返回值)
/// </summary>
/// <param name="func">業務任務帶返回值(超時要取消任務的話 需要在耗時操作之前 判斷cts如果取消就結束方法)</param>
/// <param name="timeoutSeconds">超時時間 秒</param>
/// <param name="maxRetryCount">最大重試次數</param>
/// <param name="cts">任務取消令牌</param>
/// <returns>IsSuccess:是否成功 Result:任務執行成功的結果</returns>
public static async Task<(bool IsSuccess, T Result)> TimeoutRetryAsync<T>(
Func<CancellationTokenSource, Task<T>> func, double timeoutSeconds, int maxRetryCount, CancellationTokenSource cts)
{
for (int i = 0; i <= maxRetryCount; i++)
{
if (cts.IsCancellationRequested)
break;
if (i > 0)
Console.WriteLine($"【TimeoutRetryAsync】任務第{i}次重試開始...");
CancellationTokenSource currentCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token);
Task<T> task = func.Invoke(currentCts);
Task<T> delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), currentCts.Token)
.ContinueWith(_ => default(T));
Task completeTask = await Task.WhenAny<T>(task, delayTask);
if (completeTask == task)
{
currentCts.Dispose();
return (true, await task);
}
currentCts.Cancel();
Console.WriteLine("【TimeoutRetryAsync】任務執行超時已取消。");
}
return (false, default(T));
}
}
測試用例:
public static async Task Go()
{
double timeoutSeconds = 2;//超時時間 秒
int maxRetryCount = 2;//最大重試次數
CancellationTokenSource cts = new CancellationTokenSource();
bool isSuccess = false;
string result = string.Empty;
//1.超時取消任務 無返回值
//isSuccess = await TaskExtensions.TimeoutCancelAsync((cts) => DoActionNoResult(cts), timeoutSeconds, cts);
//1.超時取消任務 無返回值
//(isSuccess, result) = await TaskExtensions.TimeoutCancelAsync((cts) => DoActionWithResult(cts), timeoutSeconds, cts);
//3.超時取消並重試任務 無返回值
//isSuccess = await TaskExtensions.TimeoutRetryAsync((cts) => DoActionNoResult(cts), timeoutSeconds, maxRetryCount, cts);
//4.超時取消並重試任務 帶返回值任務
(isSuccess, result) = await TaskExtensions.TimeoutRetryAsync((cts) => DoActionWithResult(cts), timeoutSeconds, maxRetryCount, cts);
if(isSuccess)
{
Console.WriteLine("任務執行成功,結果:" + result);
}
else
{
Console.WriteLine("任務執行失敗!");
}
Console.ReadLine();
}
public static async Task DoActionNoResult(CancellationTokenSource cts)
{
await Task.Delay(200);
for (int i = 1; i <= 5; i++)
{
if (cts.IsCancellationRequested)//在業務任務每個耗時的操作開始之前判斷取消令牌是否已取消
return;
Console.WriteLine($"num:{i}");
await Task.Delay(1000);//模擬業務操作,耗時任務。
}
}
public static async Task<string> DoActionWithResult(CancellationTokenSource cts)
{
await Task.Delay(200);
for (int i = 1; i <= 5; i++)
{
if (cts.IsCancellationRequested)//在業務任務每個耗時的操作開始之前判斷取消令牌是否已取消
return "";
Console.WriteLine($"num:{i}");
await Task.Delay(1000);//模擬業務操作,耗時任務。
}
return "666";
}
案例4-1
double timeoutSeconds = 2;//超時時間 秒
int maxRetryCount = 2;//最大重試次數
//業務方法運行時間為5.3秒左右,會一直超時 重試2次後結束
案例4-2
double timeoutSeconds = 6;//超時時間 秒
int maxRetryCount = 2;//最大重試次數
//業務方法運行時間為5.3秒左右,不會超時,會執行成功並返回結果
案例4-3
double timeoutSeconds = i+4;//超時時間 秒
int maxRetryCount = 2;//最大重試次數
//業務方法運行時間為5.3秒左右,將超時時間設置為(當前重試次數+4)。前兩次執行會超時,第三次執行成功並返回結果