Nous utilisons beaucoup de code comme celui-ci dans productio n :
var result = WaitFor<Result>.Run(1.Minutes(), () => service.GetSomeFragileResult());
L'implémentation est libre, fonctionne efficacement même dans des scénarios de calcul parallèle et est disponible en tant que partie intégrante de la base de données de l'UE. Bibliothèques partagées 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);
}
}
Ce code est encore bogué, vous pouvez essayer avec ce petit programme de test :
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());
}
}
Il y a une condition de course. Il est clairement possible qu'une ThreadAbortException soit levée après que la méthode WaitFor<int>.Run()
est appelé. Je n'ai pas trouvé de moyen fiable de résoudre ce problème, cependant, avec le même test, je ne peux pas reprocher de problème avec la fonction TheSoftwareJedi réponse acceptée.
46 votes
Juste un rappel pour ceux qui regardent les réponses ci-dessous : Beaucoup d'entre elles utilisent Thread.Abort, ce qui peut être très mauvais. Veuillez lire les différents commentaires à ce sujet avant d'implémenter Abort dans votre code. Il peut être approprié dans certaines occasions, mais celles-ci sont rares. Si vous ne comprenez pas exactement ce que fait Abort ou si vous n'en avez pas besoin, veuillez implémenter une des solutions ci-dessous qui ne l'utilise pas. Ce sont les solutions qui n'ont pas reçu autant de votes parce qu'elles ne répondaient pas aux besoins de ma question.
0 votes
Merci pour l'avis. +1 vote.
7 votes
Pour plus de détails sur les dangers de thread.Abort, lisez cet article d'Eric Lippert : blogs.msdn.com/b/ericlippert/archive/2010/02/22/