นี่คือตัวอย่างการทำงานอย่างสมบูรณ์ตามคำตอบที่ได้รับการโหวตสูงสุดซึ่งคือ:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
ข้อได้เปรียบหลักของการใช้งานในคำตอบนี้คือมีการเพิ่มข้อมูลทั่วไปดังนั้นฟังก์ชัน (หรืองาน) สามารถส่งคืนค่า ซึ่งหมายความว่าฟังก์ชันใด ๆ ที่มีอยู่สามารถถูกห่อในฟังก์ชันไทม์เอาต์เช่น
ก่อน:
int x = MyFunc();
หลังจาก:
// Throws a TimeoutException if MyFunc takes more than 1 second
int x = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
รหัสนี้ต้องใช้. NET 4.5
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTimeout
{
public static class Program
{
/// <summary>
/// Demo of how to wrap any function in a timeout.
/// </summary>
private static void Main(string[] args)
{
// Version without timeout.
int a = MyFunc();
Console.Write("Result: {0}\n", a);
// Version with timeout.
int b = TimeoutAfter(() => { return MyFunc(); },TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", b);
// Version with timeout (short version that uses method groups).
int c = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", c);
// Version that lets you see what happens when a timeout occurs.
try
{
int d = TimeoutAfter(
() =>
{
Thread.Sleep(TimeSpan.FromSeconds(123));
return 42;
},
TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", d);
}
catch (TimeoutException e)
{
Console.Write("Exception: {0}\n", e.Message);
}
// Version that works on tasks.
var task = Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(1));
return 42;
});
// To use async/await, add "await" and remove "GetAwaiter().GetResult()".
var result = task.TimeoutAfterAsync(TimeSpan.FromSeconds(2)).
GetAwaiter().GetResult();
Console.Write("Result: {0}\n", result);
Console.Write("[any key to exit]");
Console.ReadKey();
}
public static int MyFunc()
{
return 42;
}
public static TResult TimeoutAfter<TResult>(
this Func<TResult> func, TimeSpan timeout)
{
var task = Task.Run(func);
return TimeoutAfterAsync(task, timeout).GetAwaiter().GetResult();
}
private static async Task<TResult> TimeoutAfterAsync<TResult>(
this Task<TResult> task, TimeSpan timeout)
{
var result = await Task.WhenAny(task, Task.Delay(timeout));
if (result == task)
{
// Task completed within timeout.
return task.GetAwaiter().GetResult();
}
else
{
// Task timed out.
throw new TimeoutException();
}
}
}
}
คำเตือน
เมื่อได้รับคำตอบนี้โดยทั่วไปแล้วไม่ใช่วิธีปฏิบัติที่ดีที่จะมีข้อยกเว้นเกิดขึ้นในรหัสของคุณในระหว่างการทำงานปกติเว้นแต่คุณจะต้อง:
- ทุกครั้งที่มีการโยนข้อยกเว้นมันเป็นการปฏิบัติการที่หนักมาก
- ข้อยกเว้นสามารถทำให้โค้ดของคุณช้าลง 100 เท่าหรือมากกว่าหากข้อยกเว้นอยู่ในลูปที่แน่น
ใช้รหัสนี้เฉพาะในกรณีที่คุณไม่สามารถเปลี่ยนฟังก์ชั่นที่คุณโทรมาTimeSpan
ได้
คำตอบนี้ใช้ได้เฉพาะเมื่อจัดการกับห้องสมุดห้องสมุดบุคคลที่สามที่คุณไม่สามารถปรับโครงสร้างใหม่เพื่อรวมพารามิเตอร์การหมดเวลา
วิธีเขียนโค้ดที่มีประสิทธิภาพ
หากคุณต้องการเขียนโค้ดที่มีประสิทธิภาพกฎทั่วไปคือ:
ทุกการดำเนินการเดียวที่อาจปิดกั้นอย่างไม่มีกำหนดจะต้องหมดเวลา
หากคุณไม่ปฏิบัติตามกฎนี้ในที่สุดรหัสของคุณจะถูกการดำเนินการที่ล้มเหลวด้วยเหตุผลบางประการจากนั้นจะปิดกั้นไม่สิ้นสุดและแอปของคุณจะหยุดทำงานอย่างถาวร
หากมีการหมดเวลาที่เหมาะสมหลังจากนั้นสักครู่แอปของคุณจะหยุดทำงานเป็นเวลานาน (เช่น 30 วินาที) จากนั้นแอปจะแสดงข้อผิดพลาดและดำเนินการต่อไปอย่างร่าเริงหรือลองอีกครั้ง