.net构造带有超时的while循环
本文关键字:while 循环 超时 net | 更新日期: 2023-09-27 18:01:39
我通常使用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;
}
您可以使用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));
你可以把你的算法包装在一个方法中:
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
}
您真的不应该使用Sleep()
来等待任务完成。在任务完成后,这样做平均浪费了500ms。
你应该能够使用任务并行库确定地做到这一点,参见这里的例子。
这个例子展示了如何使用Wait方法,或其在任务类,等待一个单一的任务。它还展示了如何使用静态的WaitAll和WaitAny方法等待多个任务。
我不知道是否有任何现有的东西,但我认为您可以创建一个方法来接受超时和成功确定函数。像这样:
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;
}
第一个解决方案是使用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;
}
其他人提到了线程同步技术,该技术允许您等待某些任务完成。但是,如果您想继续像现在这样每秒轮询一次,您可以这样包装该方法:
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.");
}
您可以抽象地缩短代码并泛化Timeout:
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;
}
您需要使用Thread.Join。从MSDN,
阻塞调用线程,直到a线程终止,同时继续执行标准的COM和SendMessage泵。
如果你想等待直到指定的时间过去,那么使用Thread。Join (TimeSpan)方法。net 3.0, 3.5, 4.0。
阻塞调用线程,直到a线程终止或指定的时间流逝,而继续执行标准的COM和SendMessage泵。
这个关于如何在c#中使用线程的教程将会对你有所帮助。
我更喜欢异步任务方法。很多东西都被复制了,但我的测试版本:
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形参溢出!