在 C# 中实现超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10143980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 12:38:01  来源:igfitidea点击:

Implementing a timeout in c#

c#timeout

提问by Karan

I am new to c#; I have mainly done Java.

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

I want to implement a timeout something along the lines:

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

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

How can I implement this in C#? Thanks!

我如何在 C# 中实现它?谢谢!

采纳答案by Scott Chamberlain

One possible way would be:

一种可能的方法是:

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

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

However you currently have no way to break out of your loop. I would recommend having tryMethodreturn a booland change it to:

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

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

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

回答by JMK

I think you could do this with a timer and a delegate, my example code is below:

我认为您可以使用计时器和委托来做到这一点,我的示例代码如下:

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

You have tryMethod, you then create a delegate and point this delegate at tryMethod, then you start this delegate Asynchronously. Then you have a timer, with the Interval being 5000ms, you pass your delegate into your timer elapsed method (which should work as a delegate is a reference type, not an value type) and once the 5000 seconds has elapsed, you call the EndInvoke method on your delegate.

您有 tryMethod,然后创建一个委托并将此委托指向 tryMethod,然后异步启动此委托。然后你有一个定时器,间隔为 5000 毫秒,你将你的委托传递给你的定时器经过方法(它应该作为委托工作是引用类型,而不是值类型),一旦 5000 秒过去,你调用 EndInvoke您的委托的方法。

回答by JamieSee

As long as tryMethod() doesn't block this should do what you want:

只要 tryMethod() 不阻止这应该做你想要的:

Not safe for daylight savings time or changing time zones when mobile:

移动时夏令时或更改时区不安全:

DateTime startTime = DateTime.Now;

while(true)
{
    tryMethod();
    if(DateTime.Now.Subtract(startTime).TotalMilliseconds > 5000)
        throw new TimeoutException();
}

Timezone and daylight savings time safe versions:

时区和夏令时安全版本:

DateTime startTime = DateTime.UtcNow;

while(true)
{
    tryMethod();
    if(DateTime.UtcNow.Subtract(startTime).TotalMilliseconds > 5000)
        throw new TimeoutException();
} 

(.NET 3.5 or higher required for DateTimeOffset.)

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

DateTimeOffset startTime = DateTimeOffset.Now;

while(true)
{
    tryMethod();
    if(DateTimeOffset.Now.Subtract(startTime).TotalMilliseconds > 5000)
        throw new TimeoutException();
} 

回答by Samuel Ptheitroadier

Another way I like to do it:

我喜欢的另一种方式:

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

回答by fmaccaroni

Using Tasks for custom timeout on Async method

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

Here my implementation of a custom class with a method to wrap a task to have a timeout.

这里我实现了一个自定义类,它带有一个方法来包装一个任务以使其超时。

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

Then you can call it like this:

然后你可以这样称呼它:

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

or

或者

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

And you can add a cancellation token if you want to cancel the running async task if it gets to timeout.

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

Hope it helps

希望能帮助到你