.net构造for while循环与超时

时间:2011-07-08 19:06:02

标签: c# .net .net-4.0 .net-2.0

我通常使用while循环继续尝试某些操作,直到操作成功或超时为止:

bool success = false
int elapsed = 0
while( ( !success ) && ( elapsed < 10000 ) )
{
     Thread.sleep( 1000 );
     elapsed += 1000;
     success = ... some operation ...     
}

我知道有几种方法可以实现这一点,但基本的一点是我反复尝试一些操作,一直睡到成功,或者我已经睡了太长时间。

是否有内置的.net类/方法/等来阻止我在整个地方重写这个模式?也许输入是一个Func(bool)和超时?

编辑
感谢所有贡献者。我选择了sleep()方法,因为它是最不复杂的,我完全反复杂性=)这是我的(仍然需要测试)实现:

 public static bool RetryUntilSuccessOrTimeout( Func<bool> task , TimeSpan timeout , TimeSpan pause )
    {

        if ( pause.TotalMilliseconds < 0 )
        {
            throw new ArgumentException( "pause must be >= 0 milliseconds" );
        }
        var stopwatch = Stopwatch.StartNew();
        do
        {
            if ( task() ) { return true; }
            Thread.Sleep( ( int )pause.TotalMilliseconds );
        }
        while ( stopwatch.Elapsed < timeout );
        return false;
    }

9 个答案:

答案 0 :(得分:21)

您可以将算法包装在一个方法中:

public bool RetryUntilSuccessOrTimeout(Func<bool> task, TimeSpan timeSpan)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeSpan.TotalMilliseconds))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = task();
    }
    return success;
}

然后:

if (RetryUntilSuccessOrTimeout(() => SomeTask(arg1, arg2), TimeSpan.FromSeconds(10)))
{
    // the task succeeded
}

答案 1 :(得分:12)

您可以使用SpinWait.SpinUntil

请参阅https://msdn.microsoft.com/en-us/library/dd449238(v=vs.110).aspx

bool spinUntil = System.Threading.SpinWait.SpinUntil(() => job.IsDisposed, TimeSpan.FromSeconds(5));

答案 2 :(得分:5)

您真的不应该使用Sleep()等待任务完成。完成任务后,您平均浪费500毫秒。

您应该能够使用任务并行库确定性地执行此操作,例如,请参阅here

  

此示例显示如何使用Wait   方法,或其中的等价物   任务类,等待   单一任务。它还显示了如何使用   静态WaitAll和WaitAny方法   等待多项任务。

答案 3 :(得分:4)

我不知道有任何现有的东西,但我认为你可以创建一个接受超时和成功决定功能的方法。像这样:

public static bool KeepTrying(int timeout, Func<bool> operation)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeout))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = operation();
    }
    return success;
}

或者你的函数可能更“强大”,你可以将它与灵活的参数结合起来:

public bool KeepTrying(int timeout, Func<object[], bool> operation, params object[] arguments)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeout))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = operation(arguments);
    }
    return success;
}

答案 4 :(得分:2)

其他人已经提到了线程同步技术,这将允许您等到某个任务完成。但是,如果你想像你一样继续轮询每一秒,你可以像这样包装那个方法:

void Main()
{
    Timeout(() => {return false;});
}

public void Timeout(Func<bool> action, int timeout)
{
    bool success = false;
    int elapsed = 0;
    while( ( !success ) && ( elapsed < timeout ) )
    {
         Thread.Sleep( 1000 );
         elapsed += 1000;
         success = action();
    }
    Console.WriteLine("timed out.");
}

答案 5 :(得分:1)

您可以抽象缩短代码并概括超时:

int timer = 0;
while (!SomeOperation(...) && Timeout(ref timer, 1000, 10000));

public bool Timeout(ref int timer, int increment, int maximum)
{
    timer += increment;
    Thread.Sleep(increment);

    return timer < maximum;
}

答案 6 :(得分:1)

第一种解决方案是使用SpinWait

SpinWait.SpinUntil(() => LoopProcedure(), 1000);

另一种解决方案是使用Task.Wait()

var task = Task.Run(() => LoopProcedure()); 
task.Wait(1000);

将循环包装到返回bool值的过程

private bool LoopProcedure()
{
   bool success = false
   while( ( !success )
   {
      // do some stuff ...
   }
   return success;
}

答案 7 :(得分:0)

您需要使用Thread.Join。来自MSDN,

  

阻止调用线程直到a   线程终止,同时继续   执行标准COM和SendMessage   泵送。

如果要等到指定的时间过去,请使用Thread.Join (TimeSpan)方法。仅限.NET 3.0,3.5,4.0。

  

阻止调用线程直到a   线程终止或指定   时间流逝,同时继续   执行标准COM和SendMessage   泵送。

本教程如何使用Threads in C#对您有帮助。

答案 8 :(得分:0)

我更喜欢异步任务方法。复制了很多东西,但是我的测试版本是:

async Task<bool> DelayTillCondition(Func<bool> exitCondition, int delayInMs, int timeoutInSec)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeoutInSec * 1000))
    {
        await Task.Delay(delayInMs);
        elapsed += delayInMs;
        success = exitCondition();
    }
    return success;
}

和紧凑的单行方法调用:

await DelayTillCondition(() => status == "Connected", 500, 30);

请考虑此处未捕获到int参数超限!