Une solution est de l'excellente Threading in C# E-book. Dans leur section sur les structures de base, l'auteur fait presque exactement ce que vous cherchez in an example.
Suivez ce lien et descendez jusqu'à la file d'attente Producer/Consumer. Dans une section ultérieure, il explique que, bien que ConcurrentQueue fonctionnerait bien aussi, il fonctionne dans tous les cas, sauf dans des scénarios hautement concurrents. Mais pour votre cas de charge faible, il peut être préférable de faire fonctionner quelque chose facilement. Je n'ai pas d'expérience personnelle avec la revendication du document, mais vous pouvez l'évaluer.
J'espère que cela aide. Edit2: à la suggestion d'Evk (merci!), La classe BlockingCollection ressemble à ce que vous voulez. Par défaut, il utilise une ConcurrentQueue sous le capot. J'aime particulièrement la méthode CompleteAdding, ainsi que la possibilité d'utiliser CancellationTokens avec elle. Les scénarios "Shutdown" ne sont pas toujours correctement pris en compte lorsque les choses sont bloquantes, mais cela ne fonctionne pas correctement.
Édition 3: Comme demandé, un exemple de comment cela fonctionnerait avec un BlockingCollection. J'ai utilisé le foreach
et GetConsumingEnumerable
pour rendre cela encore plus compact pour le côté consommateur du problème:
using System.Collections.Concurrent;
private static void testMethod()
{
BlockingCollection<Action> myActionQueue = new BlockingCollection<Action>();
var consumer = Task.Run(() =>
{
foreach(var item in myActionQueue.GetConsumingEnumerable())
{
item(); // Run the task
}// Exits when the BlockingCollection is marked for no more actions
});
// Add some tasks
for(int i = 0; i < 10; ++i)
{
int captured = i; // Imporant to copy this value or else
myActionQueue.Add(() =>
{
Console.WriteLine("Action number " + captured + " executing.");
Thread.Sleep(100); // Busy work
Console.WriteLine("Completed.");
});
Console.WriteLine("Added job number " + i);
Thread.Sleep(50);
}
myActionQueue.CompleteAdding();
Console.WriteLine("Completed adding tasks. Waiting for consumer completion");
consumer.Wait(); // Waits for consumer to finish
Console.WriteLine("All actions completed.");
}
I ajouté dans le sommeil() appels de sorte que vous pouvez voir que les choses sont ajoutés alors que d'autres choses sont consommées . Vous pouvez également choisir de lancer n'importe quel nombre de consumer
lambda (appelez-le Action
, puis lancez le Action
plusieurs fois) ou la boucle d'addition. Et à tout moment vous pouvez appeler Count
sur la collection pour obtenir le nombre de tâches qui ne sont pas en cours d'exécution. Vraisemblablement, si ce n'est pas zéro, alors les tâches de votre producteur sont en cours d'exécution.
Regardez BlockingCollection avec ConcurrentQueue, il existe de nombreux exemples. Commencez un thread qui utilise BlockingCollection et des éléments de votre boucle principale. – Evk
La tâche et un TaskScheduler personnalisé feront probablement l'affaire si vous voulez que vos délégués soient exécutés séquentiellement. Voir http://www.infoworld.com/article/3063560/application-development/building-your-own-task-scheduler-in-c.html pour savoir comment créer un TaskScheduler personnalisé. Si cela ne vous dérange pas que vos délégués soient exécutés en parallèle, alors le .NET TaskScheduler standard est probablement très bien. –
Envisagez de conserver tout sur un thread et d'utiliser async-await pour créer un flux de travail asynchrone. Rappelez-vous, ** asynchrony n'est pas la concurrence **. Vous pouvez faire des flux de travail asynchrones sur un seul fil, tout comme vous pouvez faire cuire des œufs et des toasts en même temps sans embaucher deux cuisiniers. –