78 votes

Existe-t-il un meilleur modèle d’attente pour c #?

Je me suis retrouvé à coder ce genre de chose à quelques reprises.

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

Cela ressemble à du mauvais code, y a-t-il une meilleure façon de faire cela / est-ce un symptôme de mauvais design?

98voto

JaredPar Points 333733

Un meilleur moyen d'implémenter ce modèle consiste à laisser votre objet Thing exposer un événement sur lequel le consommateur peut attendre. Par exemple, ManualResetEvent ou AutoResetEvent . Ceci simplifie grandement votre code consommateur à être le suivant

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

// It happened
 

Le code du côté Thing n’est pas non plus plus complexe.

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

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

29voto

ChrisF Points 74295

Utilisez des événements.

Demandez à la chose que vous attendez de déclencher un événement quand il est terminé (ou ne s'est pas terminée dans le délai imparti), puis gérez l'événement dans votre application principale.

De cette façon, vous n’avez pas de boucles Sleep .

12voto

KeithS Points 36130

Une boucle n'est pas une TERRIBLE façon d'attendre quelque chose, si votre programme n'a rien d'autre à faire pendant qu'il attend (par exemple lors de la connexion à une base de données). Cependant, je vois des problèmes avec le vôtre.

     //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();
    }
 

En bref, le code ci-dessus ressemble davantage à une "boucle de nouvelle tentative", qui a été bâti pour fonctionner davantage comme un délai d'attente. Voici comment je structurerais une boucle de délai d'attente:

 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 Points 91731

Si possible, le traitement asynchrone enveloppé dans un Task<T>. Cette offre le meilleur de tous les mondes:

  • Vous pouvez répondre à l'achèvement dans un événement comme la façon en utilisant les continuations de tâches.
  • Vous pouvez attendre l'aide de l'achèvement du waitable manipuler, car Task<T> implémente IAsyncResult.
  • Les tâches sont facilement modulable à l'aide de l' Async CTP; ils jouent aussi bien avec Rx.
  • Les tâches ont une très propre intégré de gestion des exceptions du système (en particulier, ils correctement préserver la trace de la pile).

Si vous avez besoin d'utiliser un délai d'attente, puis Rx ou Async CTP peut leur fournir.

4voto

SwDevMan81 Points 22634

Je voudrais jeter un oeil à la classe WaitHandle . Plus précisément, la classe ManualResetEvent qui attend que l'objet soit défini. Vous pouvez également spécifier des valeurs de délai d'attente et vérifier s'il a été défini par la suite.

 // 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();
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X