在c#中实现超时

时间:2012-04-13 15:42:01

标签: c# timeout

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

我想实现一个超时的东西:

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

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

7 个答案:

答案 0 :(得分:36)

一种可能的方式是:

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

答案 1 :(得分: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,然后以异步方式启动此委托。然后你有一个计时器,Interval是5000ms,你将你的委托传递给你的计时器经过的方法(它应该作为一个委托是一个参考类型,而不是一个值类型),一旦5000秒过去,你调用EndInvoke代表的方法。

答案 2 :(得分:2)

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

答案 3 :(得分:2)

在Async方法上使用任务进行自定义超时

这里我实现了一个自定义类,其中包含一个将任务包装为超时的方法。

await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTask());

然后你可以这样称呼它:

var myResult = await new TaskWithTimeoutWrapper().RunWithCustomTimeoutAsync(10000, () => this.MyTaskThatReturnsMyResult());

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_async(dispatch_get_main_queue(), ^{
        self.tableView.contentOffset = CGPointMake(0, -20);
    });
}

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

希望有所帮助

答案 4 :(得分: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();
        }
    }

答案 5 :(得分:1)

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

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

从技术上讲,您还应该在CancellationToken中传播tryMethod(),以使其间断。

正在运行的演示:(请注意,由于.netfiddle不喜欢它,因此我不得不删除了引发异常的行为。)

https://dotnetfiddle.net/WjRxyk

答案 6 :(得分: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();
}