Chúng tôi đang sử dụng mã như thế này rất nhiều trong sản phẩm n:
var result = WaitFor<Result>.Run(1.Minutes(), () => service.GetSomeFragileResult());
Việc triển khai có nguồn mở, hoạt động hiệu quả ngay cả trong các tình huống tính toán song song và có sẵn như là một phần của Thư viện chia sẻ của Lokad
/// <summary>
/// Helper class for invoking tasks with timeout. Overhead is 0,005 ms.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
[Immutable]
public sealed class WaitFor<TResult>
{
readonly TimeSpan _timeout;
/// <summary>
/// Initializes a new instance of the <see cref="WaitFor{T}"/> class,
/// using the specified timeout for all operations.
/// </summary>
/// <param name="timeout">The timeout.</param>
public WaitFor(TimeSpan timeout)
{
_timeout = timeout;
}
/// <summary>
/// Executes the spcified function within the current thread, aborting it
/// if it does not complete within the specified timeout interval.
/// </summary>
/// <param name="function">The function.</param>
/// <returns>result of the function</returns>
/// <remarks>
/// The performance trick is that we do not interrupt the current
/// running thread. Instead, we just create a watcher that will sleep
/// until the originating thread terminates or until the timeout is
/// elapsed.
/// </remarks>
/// <exception cref="ArgumentNullException">if function is null</exception>
/// <exception cref="TimeoutException">if the function does not finish in time </exception>
public TResult Run(Func<TResult> function)
{
if (function == null) throw new ArgumentNullException("function");
var sync = new object();
var isCompleted = false;
WaitCallback watcher = obj =>
{
var watchedThread = obj as Thread;
lock (sync)
{
if (!isCompleted)
{
Monitor.Wait(sync, _timeout);
}
}
// CAUTION: the call to Abort() can be blocking in rare situations
// http://msdn.microsoft.com/en-us/library/ty8d3wta.aspx
// Hence, it should not be called with the 'lock' as it could deadlock
// with the 'finally' block below.
if (!isCompleted)
{
watchedThread.Abort();
}
};
try
{
ThreadPool.QueueUserWorkItem(watcher, Thread.CurrentThread);
return function();
}
catch (ThreadAbortException)
{
// This is our own exception.
Thread.ResetAbort();
throw new TimeoutException(string.Format("The operation has timed out after {0}.", _timeout));
}
finally
{
lock (sync)
{
isCompleted = true;
Monitor.Pulse(sync);
}
}
}
/// <summary>
/// Executes the spcified function within the current thread, aborting it
/// if it does not complete within the specified timeout interval.
/// </summary>
/// <param name="timeout">The timeout.</param>
/// <param name="function">The function.</param>
/// <returns>result of the function</returns>
/// <remarks>
/// The performance trick is that we do not interrupt the current
/// running thread. Instead, we just create a watcher that will sleep
/// until the originating thread terminates or until the timeout is
/// elapsed.
/// </remarks>
/// <exception cref="ArgumentNullException">if function is null</exception>
/// <exception cref="TimeoutException">if the function does not finish in time </exception>
public static TResult Run(TimeSpan timeout, Func<TResult> function)
{
return new WaitFor<TResult>(timeout).Run(function);
}
}
Mã này vẫn còn lỗi, bạn có thể thử với chương trình thử nghiệm nhỏ này:
static void Main(string[] args) {
// Use a sb instead of Console.WriteLine() that is modifying how synchronous object are working
var sb = new StringBuilder();
for (var j = 1; j < 10; j++) // do the experiment 10 times to have chances to see the ThreadAbortException
for (var ii = 8; ii < 15; ii++) {
int i = ii;
try {
Debug.WriteLine(i);
try {
WaitFor<int>.Run(TimeSpan.FromMilliseconds(10), () => {
Thread.Sleep(i);
sb.Append("Processed " + i + "\r\n");
return i;
});
}
catch (TimeoutException) {
sb.Append("Time out for " + i + "\r\n");
}
Thread.Sleep(10); // Here to wait until we get the abort procedure
}
catch (ThreadAbortException) {
Thread.ResetAbort();
sb.Append(" *** ThreadAbortException on " + i + " *** \r\n");
}
}
Console.WriteLine(sb.ToString());
}
}
Có một điều kiện chủng tộc. Rõ ràng là có thể một ThreadAdortException được nêu ra sau khi phương thức WaitFor<int>.Run()
được gọi. Tôi đã không tìm thấy một cách đáng tin cậy để khắc phục điều này, tuy nhiên với cùng một bài kiểm tra tôi không thể khắc phục bất kỳ vấn đề nào với câu trả lời được chấp nhận của TheSoftwareJedi .