Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通用重试,并在C#中超时?_C#_.net - Fatal编程技术网

通用重试,并在C#中超时?

通用重试,并在C#中超时?,c#,.net,C#,.net,我正在寻找一种通用的重试方式,在C#中有一个超时。基本上,我想要以下几点: bool stopTrying = false; DateTime time = DateTime.Now; while (!stopTrying) { try { //[Statement to Execute] } catch (Exception ex) { if (DateTime.Now.Subtract(time).Millisecond

我正在寻找一种通用的重试方式,在C#中有一个超时。基本上,我想要以下几点:

bool stopTrying = false;
DateTime time = DateTime.Now;
while (!stopTrying)
{
    try
    {
        //[Statement to Execute]
    }
    catch (Exception ex)
    {
        if (DateTime.Now.Subtract(time).Milliseconds > 10000)
        {
            stopTrying = true;
            throw ex;
        }
    }
}
在上面的例子中,我等待10秒,但它应该是一个基于参数的可变超时。我不想在需要的地方重复完整的代码。我的代码中有很多地方不是API中内置的超时,如果应用程序没有准备好执行语句,我会遇到异常。这还可以避免在这些测试之前硬编码应用程序中的延迟


澄清:所讨论的陈述可能类似于作业。如果我使用委托和方法.Invoke,那么调用的范围不是在委托内部,而不是原始方法吗?

使用您的示例,解决方案很简单:

bool DoOrTimeout<T>(T method, TimeSpan timeout) where T : delegate // FIXME
{
    bool stopTrying = false;
    DateTime time = DateTime.Now;
    while (!stopTrying)
    {
        try
        {
            method.Invoke();
            stopTrying = true;
        }
        catch (Exception ex)
        {
            if (DateTime.Now.Subtract(time).Milliseconds > timeout.TotalMilliseconds)
            {
                stopTrying = true;
                throw;
            }
        }
    }
}
bool-DoOrTimeout(T方法,TimeSpan-timeout),其中T:delegate//FIXME
{
bool stopTrying=false;
DateTime=DateTime.Now;
而(!停止尝试)
{
尝试
{
方法调用();
停止尝试=正确;
}
捕获(例外情况除外)
{
if(DateTime.Now.Subtract(time).millizes>timeout.totalmillizes)
{
停止尝试=正确;
投掷;
}
}
}
}

只需使用委托作为第一个参数调用
DoOrTimeout

创建一个方法,该方法使用lambda表达式执行语句,并使用参数超时。在该方法中,在try/catch块中执行lambda表达式,并使用参数超时。

这不是最漂亮的事情,但到目前为止我似乎工作得很好。并且它不使用异常来指示超时

public static class TimeoutOperation
{
  private static readonly TimeSpan DefaultTimeout = new TimeSpan(0, 0, 10);
  private static readonly TimeSpan DefaultGranularity = new TimeSpan(0, 0, 0, 0, 100);

  public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action)
  {
    return DoWithTimeout<TResult>(action, DefaultTimeout);
  }

  public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action, TimeSpan timeout)
  {
    return DoWithTimeout<TResult>(action, timeout, DefaultGranularity);
  }

  public static ThreadResult<TResult> DoWithTimeout<TResult>(Func<TResult> action, TimeSpan timeout, TimeSpan granularity)
  {
    Thread thread = BuildThread<TResult>(action);
    Stopwatch stopwatch = Stopwatch.StartNew();
    ThreadResult<TResult> result = new ThreadResult<TResult>();

    thread.Start(result);
    do
    {
      if (thread.Join(granularity) && !result.WasSuccessful)
      {
        thread = BuildThread<TResult>(action);
        thread.Start(result);
      }

    } while (stopwatch.Elapsed < timeout && !result.WasSuccessful);
    stopwatch.Stop();

    if (thread.ThreadState == System.Threading.ThreadState.Running)
      thread.Abort();

    return result;
  }

  private static Thread BuildThread<TResult>(Func<TResult> action)
  {
    return new Thread(p =>
    {
      ThreadResult<TResult> r = p as ThreadResult<TResult>;
      try { r.Result = action(); r.WasSuccessful = true; }
      catch (Exception) { r.WasSuccessful = false; }
    });
  }

  public class ThreadResult<TResult>
  {
    public TResult Result { get; set; }
    public bool WasSuccessful { get; set; }
  }
}
公共静态类超时操作
{
私有静态只读TimeSpan DefaultTimeout=新的TimeSpan(0,0,10);
私有静态只读TimeSpan DefaultGranularity=新TimeSpan(0,0,0,0,100);
公共静态ThreadResult DoWithTimeout(函数操作)
{
返回DoWithTimeout(操作,默认超时);
}
公共静态ThreadResult DoWithTimeout(Func操作,TimeSpan超时)
{
返回DoWithTimeout(操作、超时、默认粒度);
}
公共静态ThreadResult DoWithTimeout(Func操作、TimeSpan超时、TimeSpan粒度)
{
线程线程=构建线程(操作);
秒表秒表=Stopwatch.StartNew();
ThreadResult=新的ThreadResult();
线程开始(结果);
做
{
if(thread.Join(粒度)&&!result.wassuctive)
{
线程=构建线程(操作);
线程开始(结果);
}
}while(stopwatch.appeased
{
ThreadResult r=p作为ThreadResult;
请尝试{r.Result=action();r.wassucsuccessful=true;}
捕获(异常){r.wassuctive=false;}
});
}
公共类ThreadResult
{
公共TResult结果{get;set;}
公共bool已成功{get;set;}
}
}
用法
var result=TimeoutOperation.DoWithTimeout(()=>
{
睡眠(100);
抛出新异常();
});
result.WasSuccessful/=false
result.Value/=0
var result=TimeoutOperation.DoWithTimeout(()=>
{
《睡眠》(2000年);
返回5;
});
result.WasSuccessful/=true
结果.值//=5

看看这个问题。你的要求正是我想要的用途之一。

警告:此示例使用Thread.Abort。按照我最初问题的链接阅读评论中的一些警告

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Something
{
  public static class TimeoutWrapper
  {
    public static void Invoke(TimeSpan timeout, Action action)
    {
      Invoke(timeout, action, null);
    }
    public static void Invoke(TimeSpan timeout, Action action, Action abort)
    {
      Thread threadToKill = null;
      Action wrappedAction = () =>
      {
        threadToKill = Thread.CurrentThread;
        action();
      };

      IAsyncResult result = wrappedAction.BeginInvoke(null, null);
      if (result.AsyncWaitHandle.WaitOne(timeout, true))
      {
        wrappedAction.EndInvoke(result);
      }
      else
      {
        if (threadToKill != null)
        {
          try { threadToKill.Abort(); }
          catch { /* Ignore */ }
        }

        if (abort != null)
          abort();

        throw new TimeoutException();
      }
    }
  }
}
只要在一个循环中使用适当的超时控制来运行它

DateTime endAt = DateTime.Now.AddMinutes(1);
Timespan timeout = new Timespan( 0, 0, 0, 5);
while( DateTime.Now < endAt )
{
    try
    {
        TimeoutWrapper.Invoke( timeout, () => DoSomething());
        break;
    } 
    catch( TimeoutException ex ) 
    { /* Do something */ }
}
DateTime endAt=DateTime.Now.AddMinutes(1);
Timespan超时=新的Timespan(0,0,0,5);
while(DateTime.NowDoSomething());
打破
} 
捕获(TimeoutException例外)
{/*做点什么*/}
}
此代码错误(无限循环):

正确的答案是:

if (DateTime.Now.Subtract(time).TotalMilliseconds > 10000)

以下是一个简单的解决方案:

long TIMEOUT = 60000; // 1 minute
long INTERVAL = 1000; // 1 second

System.DateTime startTime = System.DateTime.Now;    

while (check_condition())
{
    System.Threading.Thread.Sleep(INTERVAL);
    long elapsedTime = System.DateTime.Now.Millisecond - startTime.Millisecond;

    if (elapsedTime > TIMEOUT)
    {
        throw new Exception("Timeout exceeded");
    }
}

请使用
throw
重新抛出异常,而不是
抛出异常因为后者破坏堆栈跟踪。@威尔,我使用了OP的代码。不过,你是对的<代码>抛出
应单独使用。我将更新我的答案以反映这一点。您缺少一个stopTrying=true;在调用“method.Invoke()”之后;这段代码无法编译。无法将委托用作约束。将一个
TimeSpan
上的
毫秒数
与另一个
总毫秒数
进行比较必须是一个输入错误(错误)。为什么不直接比较
TimeSpan
?该类型重载了常用的运算符。因此:
if(DateTime.Now-time>timeout){…}
更为正确。如果不想处理异常,也可以直接“中断”而不是抛出异常。
if (DateTime.Now.Subtract(time).TotalMilliseconds > 10000)
long TIMEOUT = 60000; // 1 minute
long INTERVAL = 1000; // 1 second

System.DateTime startTime = System.DateTime.Now;    

while (check_condition())
{
    System.Threading.Thread.Sleep(INTERVAL);
    long elapsedTime = System.DateTime.Now.Millisecond - startTime.Millisecond;

    if (elapsedTime > TIMEOUT)
    {
        throw new Exception("Timeout exceeded");
    }
}