78 Stimmen

Gibt es ein besseres Warteschema für c#?

Ich habe diese Art von Kodierung schon ein paar Mal erlebt.

for (int i = 0; i < 10; i++)
{
   if (Thing.WaitingFor())
   {
      break;
   }
   Thread.Sleep(sleep_time);
}
if(!Thing.WaitingFor())
{
   throw new ItDidntHappenException();
}

Es sieht einfach nach schlechtem Code aus, gibt es einen besseren Weg, dies zu tun / ist es ein Symptom für schlechtes Design?

99voto

JaredPar Punkte 699699

Eine viel bessere Möglichkeit, dieses Muster zu implementieren, besteht darin, dass Ihre Thing Objekt ein Ereignis aus, auf das der Verbraucher warten kann. Zum Beispiel ein ManualResetEvent o AutoResetEvent . Dies vereinfacht Ihren Verbrauchercode erheblich und sieht wie folgt aus

if (!Thing.ManualResetEvent.WaitOne(sleep_time)) {
  throw new ItDidntHappen();
}

// It happened

Der Code auf dem Thing Seite ist auch nicht wirklich komplexer.

public sealed class Thing {
  public readonly ManualResetEvent ManualResetEvent = new ManualResetEvent(false);

  private void TheAction() {
    ...
    // Done.  Signal the listeners
    ManualResetEvent.Set();
  }
}

29voto

ChrisF Punkte 130622

Ereignisse verwenden.

Lassen Sie die Sache, auf die Sie warten, ein Ereignis auslösen, wenn sie fertig ist (oder nicht innerhalb der vorgegebenen Zeit fertig wurde), und behandeln Sie das Ereignis dann in Ihrer Hauptanwendung.

Auf diese Weise haben Sie keine Sleep Schleifen.

12voto

KeithS Punkte 67713

Eine Schleife ist kein schlechter Weg, um auf etwas zu warten, wenn es für Ihr Programm nichts anderes zu tun gibt, während es wartet (zum Beispiel während der Verbindung zu einer DB). Ich sehe jedoch einige Probleme mit Ihrem Programm.

    //It's not apparent why you wait exactly 10 times for this thing to happen
    for (int i = 0; i < 10; i++)
    {
        //A method, to me, indicates significant code behind the scenes.
        //Could this be a property instead, or maybe a shared reference?
        if (Thing.WaitingFor()) 
        {
            break;
        }
        //Sleeping wastes time; the operation could finish halfway through your sleep. 
        //Unless you need the program to pause for exactly a certain time, consider
        //Thread.Yield().
        //Also, adjusting the timeout requires considering how many times you'll loop.
        Thread.Sleep(sleep_time);
    }
    if(!Thing.WaitingFor())
    {
        throw new ItDidntHappenException();
    }

Kurz gesagt, der obige Code sieht eher wie eine "Wiederholungsschleife" aus, die so verfälscht wurde, dass sie eher wie ein Timeout funktioniert. Hier ist, wie ich eine Timeout-Schleife strukturieren würde:

var complete = false;
var startTime = DateTime.Now;
var timeout = new TimeSpan(0,0,30); //a thirty-second timeout.

//We'll loop as many times as we have to; how we exit this loop is dependent only
//on whether it finished within 30 seconds or not.
while(!complete && DateTime.Now < startTime.Add(timeout))
{
   //A property indicating status; properties should be simpler in function than methods.
   //this one could even be a field.
   if(Thing.WereWaitingOnIsComplete)
   {
      complete = true;
      break;
   }

   //Signals the OS to suspend this thread and run any others that require CPU time.
   //the OS controls when we return, which will likely be far sooner than your Sleep().
   Thread.Yield();
}
//Reduce dependence on Thing using our local.
if(!complete) throw new TimeoutException();

9voto

Stephen Cleary Punkte 402664

Wenn möglich, sollten Sie die asynchrone Verarbeitung in eine Task<T> . Dies bietet das Beste aus allen Welten:

  • Sie können auf den Abschluss ereignisähnlich reagieren, indem Sie Aufgabenfortsetzungen .
  • Sie können mit dem waitable-Handle des Abschlusses warten, weil Task<T> implementiert IAsyncResult .
  • Aufgaben sind leicht zusammensetzbar unter Verwendung der Async CTP ; sie spielen auch gut mit Rx .
  • Tasks haben ein sehr sauberes eingebautes System zur Behandlung von Ausnahmen (insbesondere wird der Stack-Trace korrekt beibehalten).

Wenn Sie eine Zeitüberschreitung benötigen, können Rx oder der Async CTP diese bereitstellen.

5voto

SwDevMan81 Punkte 47339

Ich würde einen Blick auf die WaitHandle Klasse. Insbesondere die ManualResetEvent Klasse, die wartet, bis das Objekt gesetzt ist. Sie können auch Timeout-Werte für die Klasse angeben und danach prüfen, ob sie gesetzt wurde.

// Member variable
ManualResetEvent manual = new ManualResetEvent(false); // Not set

// Where you want to wait.
manual.WaitOne(); // Wait for manual.Set() to be called to continue here
if(!manual.WaitOne(0)) // Check if set
{
   throw new ItDidntHappenException();
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X