16

我是 C# 的新手;我主要做Java。

我想按照以下方式实现超时:

int now= Time.now();
while(true)
{
  tryMethod();
  if(now > now+5000) throw new TimeoutException();
}

如何在 C# 中实现这一点?谢谢!

4

7 回答 7

40

一种可能的方法是:

Stopwatch sw = new Stopwatch();
sw.Start();

while(true)
{
    tryMethod();
    if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}

但是,您目前无法摆脱循环。我建议tryMethod返回 abool并将其更改为:

Stopwatch sw = new Stopwatch();
sw.Start();

while(!tryMethod())
{
    if(sw.ElapsedMilliseconds > 5000) throw new TimeoutException();
}
于 2012-04-13T15:44:26.170 回答
6

我认为您可以使用计时器和委托来执行此操作,我的示例代码如下:

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,然后你异步启动这个委托。然后你有一个计时器,间隔为 5000 毫秒,你将你的委托传递给你的计时器经过的方法(它应该作为一个委托是一个引用类型,而不是一个值类型),一旦 5000 秒过去了,你调用 EndInvoke您的委托上的方法。

于 2012-04-13T15:59:44.710 回答
6

这个问题很老了,但还有另一种选择。

using(CancellationTokenSource cts = new CancellationTokenSource(5000))
{
  cts.Token.Register(() => { throw new TimeoutException(); });
  while(!cts.IsCancellationRequested)
  {
    tryMethod();
  }
}

从技术上讲,您还应该传播CancellationTokenintryMethod()以优雅地中断它。

工作演示:(注意我必须删除异常抛出行为,因为 .netfiddle 不喜欢它。)

https://dotnetfiddle.net/WjRxyk

于 2020-07-20T18:47:14.663 回答
4

只要 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();
} 

(DateTimeOffset 需要 .NET 3.5 或更高版本。)

DateTimeOffset startTime = DateTimeOffset.Now;

while(true)
{
    tryMethod();
    if(DateTimeOffset.Now.Subtract(startTime).TotalMilliseconds > 5000)
        throw new TimeoutException();
} 
于 2012-04-13T16:06:56.490 回答
3

在异步方法上使用任务自定义超时

在这里,我实现了一个自定义类,该类具有包装任务以实现超时的方法。

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());

如果您想在超时时取消正在运行的异步任务,您可以添加一个取消令牌。

希望能帮助到你

于 2017-09-11T20:05:03.490 回答
1

我喜欢做的另一种方式:

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();
        }
    }
于 2014-03-05T19:22:33.427 回答
0
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(10000);
try
{
    Task task = Task.Run(() => { methodToTimeoutAfter10Seconds(); }, cts.Token);
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
    using (cts.Token.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
    {
        if (task != await Task.WhenAny(task, tcs.Task))
        {
            throw new OperationCanceledException(cts.Token);
        }
    }
    /* Wait until the task is finish or timeout. */
    task.Wait();

    /* Rest of the code goes here */
    
}
catch (TaskCanceledException)
{
    Console.WriteLine("Timeout");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Timeout");
}
catch (Exception ex)
{
    Console.WriteLine("Other exceptions");
}
finally
{
    cts.Dispose();
}   
于 2021-07-14T01:44:16.620 回答