web-dev-qa-db-ja.com

タイムアウト付きのwhileループの.net構造

私は通常、操作が成功するかタイムアウトが経過するまで何らかの操作を試行し続けるwhileループを使用します。

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

これを実装する方法はいくつかありますが、基本的なポイントは、成功するまでスリープ状態でいくつかの操作を繰り返し試行すること、または合計で睡眠時間が長すぎることです。

組み込みの.netクラス/メソッド/ etcを使用して、このパターンをあちこちで書き換える必要をなくしていますか?おそらく、入力は(ブール値の)Funcとタイムアウトですか?

編集する
貢献してくれたすべての人に感謝します。私は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;
    }
22
SFun28

アルゴリズムをメソッドでラップすることができます:

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
}
22
Darin Dimitrov

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));
19
Michael Baker

タスクが完了するまでSleep()を使用する必要はありません。これを実行すると、タスクが完了してから平均500ミリ秒を無駄にします。

タスク並列ライブラリを使用してこれを確定的に行うことができるはずです。たとえば here を参照してください。

この例は、Waitメソッド、またはTaskクラスの同等のメソッドを使用して、単一のタスクで待機する方法を示しています。また、静的なWaitAllメソッドとWaitAnyメソッドを使用して複数のタスクを待機する方法も示します。

5
Steve Townsend

既存のものがあるかどうかはわかりませんが、タイムアウトと成功判定関数を受け入れるメソッドを作成できると思います。このようなもの:

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
Steven

最初の解決策は、SpinWaitを使用することです

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

他の解決策はTask.Wait()を使用することです

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

ループをブール値を返すプロシージャにラップします

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

他の人は、いくつかのタスクが完了するまで待つことができるスレッド同期技術について言及しました。ただし、毎秒ポーリングを継続する場合は、次のようにそのメソッドをラップできます。

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

抽象化してコードを少し短くし、タイムアウトを一般化できます。

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;
}
1
m-y

Thread.Join を使用する必要があります。 MSDNから、

スレッドが終了するまで呼び出しスレッドをブロックし、その間、標準のCOMおよびSendMessageポンプを実行し続けます。

指定した時間が経過するまで待機する場合は、 Thread.Join(TimeSpan) メソッドを使用します。 .NET 3.0、3.5、4.0のみ。

スレッドが終了するか、指定された時間が経過するまで、標準のCOMとSendMessageのポンピングを実行し続ける間、呼び出しスレッドをブロックします。

この使用方法に関するチュートリアル C#のスレッド が役立ちます。

0
user195488