Реализация тайм-аута в c#
Я новичок в c#; я в основном сделал Java.
Я хочу реализовать тайм-аут что-то по строкам:
int now= Time.now();
while(true)
{
tryMethod();
if(now > now+5000) throw new TimeoutException();
}
Как я могу реализовать это в C#? Спасибо!
5 ответов
один из возможных способов такой:
Stopwatch sw = new Stopwatch();
sw.Start();
while(true)
{
tryMethod();
if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}
однако в настоящее время у вас нет возможности вырваться из вашего цикла. Я бы рекомендовал иметь tryMethod
возвратить bool
и меняем его на:
Stopwatch sw = new Stopwatch();
sw.Start();
while(!tryMethod())
{
if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}
Я думаю, вы могли бы сделать это с таймером и делегатом, мой пример кода ниже:
using System;
using System.Timers;
class Program
{
public delegate void tm();
static void Main(string[] args)
{
var t = new tm(tryMethod);
var timer = new Timer();
timer.Interval = 5000;
timer.Start();
timer.Elapsed += (sender, e) => timer_Elapsed(t);
t.BeginInvoke(null, null);
}
static void timer_Elapsed(tm p)
{
p.EndInvoke(null);
throw new TimeoutException();
}
static void tryMethod()
{
Console.WriteLine("FooBar");
}
}
у вас есть tryMethod, затем вы создаете делегат и указываете этот делегат на tryMethod, затем запускаете этот делегат асинхронно. Затем у вас есть таймер с интервалом 5000ms, вы передаете свой делегат в свой метод таймера (который должен работать как делегат является ссылочным типом, а не типом значения), и как только 5000 секунд истекло, вы вызываете метод EndInvoke на вашего делегата.
пока tryMethod () не блокирует, это должно делать то, что вы хотите:
небезопасно для летнего времени или изменения часовых поясов, когда мобильный:
DateTime startTime = DateTime.Now;
while(true)
{
tryMethod();
if(DateTime.Now.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
часовой пояс и летнее время безопасные версии:
DateTime startTime = DateTime.UtcNow;
while(true)
{
tryMethod();
if(DateTime.UtcNow.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
(.NET 3.5 или выше требуется для DateTimeOffset.)
DateTimeOffset startTime = DateTimeOffset.Now;
while(true)
{
tryMethod();
if(DateTimeOffset.Now.Subtract(startTime).TotalMilliseconds > 5000)
throw new TimeoutException();
}
другой способ мне нравится это делать:
public class TimeoutAction
{
private Thread ActionThread { get; set; }
private Thread TimeoutThread { get; set; }
private AutoResetEvent ThreadSynchronizer { get; set; }
private bool _success;
private bool _timout;
/// <summary>
///
/// </summary>
/// <param name="waitLimit">in ms</param>
/// <param name="action">delegate action</param>
public TimeoutAction(int waitLimit, Action action)
{
ThreadSynchronizer = new AutoResetEvent(false);
ActionThread = new Thread(new ThreadStart(delegate
{
action.Invoke();
if (_timout) return;
_timout = true;
_success = true;
ThreadSynchronizer.Set();
}));
TimeoutThread = new Thread(new ThreadStart(delegate
{
Thread.Sleep(waitLimit);
if (_success) return;
_timout = true;
_success = false;
ThreadSynchronizer.Set();
}));
}
/// <summary>
/// If the action takes longer than the wait limit, this will throw a TimeoutException
/// </summary>
public void Start()
{
ActionThread.Start();
TimeoutThread.Start();
ThreadSynchronizer.WaitOne();
if (!_success)
{
throw new TimeoutException();
}
ThreadSynchronizer.Close();
}
}
использование задач для пользовательского таймаута в асинхронном методе
здесь моя реализация пользовательского класса с методом обертывания задачи, чтобы иметь тайм-аут.
public class TaskWithTimeoutWrapper
{
protected volatile bool taskFinished = false;
public async Task<T> RunWithCustomTimeoutAsync<T>(int millisecondsToTimeout, Func<Task<T>> taskFunc, CancellationTokenSource cancellationTokenSource = null)
{
this.taskFinished = false;
var results = await Task.WhenAll<T>(new List<Task<T>>
{
this.RunTaskFuncWrappedAsync<T>(taskFunc),
this.DelayToTimeoutAsync<T>(millisecondsToTimeout, cancellationTokenSource)
});
return results[0];
}
public async Task RunWithCustomTimeoutAsync(int millisecondsToTimeout, Func<Task> taskFunc, CancellationTokenSource cancellationTokenSource = null)
{
this.taskFinished = false;
await Task.WhenAll(new List<Task>
{
this.RunTaskFuncWrappedAsync(taskFunc),
this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource)
});
}
protected async Task DelayToTimeoutAsync(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource)
{
await Task.Delay(millisecondsToTimeout);
this.ActionOnTimeout(cancellationTokenSource);
}
protected async Task<T> DelayToTimeoutAsync<T>(int millisecondsToTimeout, CancellationTokenSource cancellationTokenSource)
{
await this.DelayToTimeoutAsync(millisecondsToTimeout, cancellationTokenSource);
return default(T);
}
protected virtual void ActionOnTimeout(CancellationTokenSource cancellationTokenSource)
{
if (!this.taskFinished)
{
cancellationTokenSource?.Cancel();
throw new NoInternetException();
}
}
protected async Task RunTaskFuncWrappedAsync(Func<Task> taskFunc)
{
await taskFunc.Invoke();
this.taskFinished = true;
}
protected async Task<T> RunTaskFuncWrappedAsync<T>(Func<Task<T>> taskFunc)
{
var result = await taskFunc.Invoke();
this.taskFinished = true;
return result;
}
}
затем вы можете назвать это так:
await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTask());
или
var myResult = await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTaskThatReturnsMyResult());
и вы можете добавить токен отмены, если хотите отменить запущенную асинхронную задачу, если она получит тайм-аут.
надеюсь, это поможет