2017-10-10 12 views
1

J'essaie de construire une sorte de planificateur (ce n'est peut-être pas le terme pertinent) qui exécuterait en séquence un certain nombre de tâches.C# 6 en attente de l'achèvement complet de la tâche et toutes les sous-tâches

Voici mon code POC (s'il vous plaît ignorer le mécanisme file d'attente/dequeue qui est pauvre, mais pas le problème ici, je suppose)

EDIT: grâce à l'aide de @Theraot

static void Main(string[] args) 
    { 
     ProcessingQueue o_q = new ProcessingQueue(); 
     o_q.Enqueue(async() => { await SimulateTaskSequence(1); }); 
     o_q.Enqueue(async() => { await SimulateTaskSequence(2); }); 

     Console.ReadLine(); 
    } 

    public static async Task SimulateTaskSequence(int taskNbr) 
    { 
     Console.WriteLine("T{0} - Working 1sec", taskNbr); 
     Thread.Sleep(1000); 

     Console.WriteLine("T{0} - Zzz 1st 1sec", taskNbr); 
     await Task.Delay(1000); 

     Console.WriteLine("T{0} - Working 1sec", taskNbr); 
     Thread.Sleep(1000); 

     Console.WriteLine("T{0} - Done", taskNbr); 
    } 

    public class ProcessingQueue 
    { 
     Queue<Action> _Queue = new Queue<Action>(); 
     private bool _stillRunning = false; 

     public void Enqueue(Action a) 
     { 
      lock (_Queue) 
      { 
       _Queue.Enqueue(a); 

       if (_stillRunning == false) 
       { 
        StartProcessing(); 
       } 
      } 
     } 


     private void StartProcessing() 
     { 
      _stillRunning = true; 

      Task.Run(async() => 
      { 
       Action a = null; 

       while (true) 
       { 
        lock (_Queue) 
        { 
         if (_Queue.Any() == true) 
         { 
          a = _Queue.Dequeue(); 
         } 
         else 
         { 
          break; 
         } 
        } 

        await Task.Run(a); //how to wait for all subtasks!!??? 
       } 
       _stillRunning = false; 
      }); 
     } 

Mon problème est que dès que le premier attentat de la première tâche (T1) se produit, les deuxièmes tâches (T2) commencent à être exécutées.

je reçois la sortie suivante:

T1 - Working 1sec 
T1 - Zzz 1st 1sec 
T2 - Working 1sec 
T2 - Zzz 1st 1sec 
T1 - Working 1sec 
T1 - Done 
T2 - Working 1sec 
T2 - Done 

Mais ce que je prévois serait:

T1 - Working 1sec 
T1 - Zzz 1st 1sec 
T1 - Working 1sec 
T1 - Done 
T2 - Working 1sec 
T2 - Zzz 1st 1sec 
T2 - Working 1sec 
T2 - Done 

Je comprends pourquoi cela est le comportement par défaut, mais je dois changer cela. Je jouais autour de TaskContinuationOptions et TaskCreationOptions dans une nouvelle TaskFactory, mais sans de meilleurs résultats. Est-ce encore possible?

Merci beaucoup Christophe

+0

Votre classe ProcessingQueue existe déjà dans le cadre, il est ThreadPool. Difficile à remplacer correctement, et il ne fait rien que ThreadPool ne fasse pas. Autre que l'attente et qui s'est rapidement mal passé. Ne fais pas ça. –

+0

@HansPassant Ce que OP veut est différent du ThreadPool en ce que OP souhaite que les tâches ajoutées se terminent séquentiellement. Sur le ThreadPool vous n'avez pas une telle garantie, en fait l'idée du ThreadPool est qu'il utilise plusieurs Threads pour exécuter les choses en parallèle (edit: et réutilise les threads, bien sûr). Maintenant, cela peut être accompli en exécutant ces tâches de manière synchrone, ou par d'autres moyens qui utilisent un Thread pour attendre ... OP ne veut pas non plus de ça. – Theraot

+0

La classe Task se compose très bien, tout ce dont il a besoin est ContinueWith pour séquencer la tâche principale et WaitAll pour attendre la fin des sous-tâches. Facile, mais cela devient difficile à voir quand vous inventez votre propre pool de threads. –

Répondre

2

Je suggère la création d'un ProcessingQueue<Func<Task>> au lieu de ProcessingQueue<Action>

public class ProcessingQueue 
{ 
    Queue<Func<Task>> _Queue = new Queue<Func<Task>>(); 

    private bool _stillRunning = false; 

    public void Enqueue(Func<Task> a) 
    { 
     lock (_Queue) 
     { 
      _Queue.Enqueue(a); 

      if (_stillRunning == false) 
      { 
       StartProcessing(); 
      } 
     } 
    } 

    private void StartProcessing() 
    { 
     _stillRunning = true; 

     Task.Run(async() => 
     { 
      Func<Task> a = null; 

      while (true) 
      { 
       lock (_Queue) 
       { 
        if (_Queue.Any() == true) 
        { 
         a = _Queue.Dequeue(); 
        } 
        else 
        { 
         break; 
        } 
       } 

       await a(); //how to wait for all subtasks!!??? 
      } 
      _stillRunning = false; 
     }); 
    } 

Explication

Dans le code écrit en question,

Action a; 
... 
await Task.Run(a); 

Vous exécutez Task.Run(Action action), puisque l'action peut contenir une tâche asynchrone, la méthode Run n'attend pas la tâche car il n'y a pas de tâche. Lorsque vous appelez Task.Run(Func<Task> task)Run méthode sait que c'est tâche et il attendra,

+0

@Theraot merci de souligner –