2009-06-24 4 views
3

J'ai rencontré ce problème dans plusieurs langages de programmation et je me demandais juste quelle est la meilleure façon de le gérer.Faire plusieurs appels asynchrones et faire quelque chose quand ils sont terminés

J'ai trois appels de méthode qui se déclenchent de manière asynchrone. Chacun a un rappel. Je veux faire quelque chose seulement quand les trois rappels sont terminés.

Quel est le meilleur moyen de coder ceci? Habituellement, je me retrouve avec tous ces drapeaux publics et lorsque vous ajoutez d'autres appels, le code devient plus compliqué.

Répondre

3

En provenance de C#, j'utiliserais probablement WaitHandle.WaitAll. Vous pouvez créer un tableau d'objets ManualResetEvent (un pour chaque tâche à compléter) et transmettre ce tableau à WaitAll. Les tâches threadées recevront chacune un objet ManualResetEvent et appellent la méthode Set lorsqu'elles sont prêtes. WaitAll va bloquer le thread appelant jusqu'à ce que toutes les tâches soient terminées. Je vais donner un exemple de code C#:

private void SpawnWorkers() 
{ 
    ManualResetEvent[] resetEvents = new[] { 
      new ManualResetEvent(false), 
      new ManualResetEvent(false) 
     }; 

    // spawn the workers from a separate thread, so that 
    // the WaitAll call does not block the main thread 
    ThreadPool.QueueUserWorkItem((state) => 
    { 
     ThreadPool.QueueUserWorkItem(Worker1, resetEvents[0]); 
     ThreadPool.QueueUserWorkItem(Worker2, resetEvents[1]); 
     WaitHandle.WaitAll(resetEvents); 
     this.BeginInvoke(new Action(AllTasksAreDone)); 

    }); 
} 
private void AllTasksAreDone() 
{ 
    // OK, all are done, act accordingly 
} 

private void Worker1(object state) 
{ 
    // do work, and then signal the waiting thread 
    ((ManualResetEvent) state).Set(); 
} 

private void Worker2(object state) 
{ 
    // do work, and then signal the waiting thread 
    ((ManualResetEvent)state).Set(); 
} 

Notez que la méthode AllTasksAreDone exécutera sur le fil de la piscine de fil qui a été utilisé pour frayer les travailleurs, et non sur le fil conducteur ... Je suppose que beaucoup d'autres les langues ont des constructions similaires.

+0

Dans quel contexte sommes-nous lors de l'appel this.BeginInvoke? Qu'est-ce que c'est'? –

1

Si vous voulez vraiment qu'à attendre tous pour terminer:

  1. Créer compteur volatile
  2. accès Synchronize pour contrer
  3. Augmentation contre le début
  4. Diminution de rappel a tiré
  5. Attendre que le compteur atteigne 0
0

Les contrats à terme sont très faciles à utiliser. Futures ressemblent à des fonctions normales, sauf qu'ils exécutent asynch.

Les classes:

public struct FutureResult<T> 
{ 
    public T Value; 
    public Exception Error; 
} 
public class Future<T> 
{ 
    public delegate R FutureDelegate<R>(); 
    public Future(FutureDelegate<T> del) 
    { 
     _del = del; 
     _result = del.BeginInvoke(null, null); 
    } 
    private FutureDelegate<T> _del; 
    private IAsyncResult _result; 
    private T _persistedValue; 
    private bool _hasValue = false; 
    private T Value 
    { 
     get 
     { 
      if (!_hasValue) 
      { 
       if (!_result.IsCompleted) 
        _result.AsyncWaitHandle.WaitOne(); 
       _persistedValue = _del.EndInvoke(_result); 
       _hasValue = true; 
      } 
      return _persistedValue; 
     } 
    } 
    public static implicit operator T(Future<T> f) 
    { 
     return f.Value; 
    } 
} 

J'utilise ici à terme pour simuler une impasse:


     void SimulateDeadlock() 
     { 
      Future> deadlockFuture1 = new Future>(() => 
      { 
       try 
       { 
        new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString) 
         .SimulateDeadlock1(new DateTime(2000, 1, 1, 0, 0, 2)); 
        return new FutureResult { Value = true }; 
       } 
       catch (Exception ex) 
       { 
        return new FutureResult { Value = false, Error = ex }; 
       } 
      }); 
      Future> deadlockFuture2 = new Future>(() => 
      { 
       try 
       { 
        new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString) 
         .SimulateDeadlock2(new DateTime(2000, 1, 1, 0, 0, 2)); 
        return new FutureResult { Value = true }; 
       } 
       catch (Exception ex) 
       { 
        return new FutureResult { Value = false, Error = ex }; 
       } 
      }); 
      FutureResult result1 = deadlockFuture1; 
      FutureResult result2 = deadlockFuture2; 
      if (result1.Error != null) 
      { 
       if (result1.Error is SqlException && ((SqlException)result1.Error).Number == 1205) 
        Console.WriteLine("Deadlock!"); 
       else 
        Console.WriteLine(result1.Error.ToString()); 
      } 
      else if (result2.Error != null) 
      { 
       if (result2.Error is SqlException && ((SqlException)result2.Error).Number == 1205) 
        Console.WriteLine("Deadlock!"); 
       else 
        Console.WriteLine(result2.Error.ToString()); 
      } 
     } 

+0

certaines des classes sont manquantes dans cet exemple – mcintyre321