J'ai une collection de 1000 messages d'entrée à traiter. Je boucle la collection d'entrée et lance la nouvelle tâche pour que chaque message soit traité.
//Assume this messages collection contains 1000 items
var messages = new List<string>();
foreach (var msg in messages)
{
Task.Factory.StartNew(() =>
{
Process(msg);
});
}
Pouvons-nous deviner combien de messages maximum sont traités simultanément à la fois (en supposant un processeur Quad Core normal), ou pouvons-nous limiter le nombre maximum de messages à traiter à la fois?
Comment faire en sorte que ce message soit traité dans le même ordre/séquence de la collection?
SemaphoreSlim est une très bonne solution dans ce cas et je recommande vivement à OP d’essayer, mais la réponse de @ Manoj a le défaut mentionné dans comments.semaphore doit être attendu avant de générer une telle tâche.
Réponse mise à jour: Comme @Vasyl l'a souligné, Semaphore peut être supprimé avant l'achèvement des tâches et déclenche une exception lorsque la méthode Release()
est appelée. Avant de quitter le bloc using, vous devez attendre l'achèvement de toutes les tâches créées.
int maxConcurrency=10;
var messages = new List<string>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach(var msg in messages)
{
concurrencySemaphore.Wait();
var t = Task.Factory.StartNew(() =>
{
try
{
Process(msg);
}
finally
{
concurrencySemaphore.Release();
}
});
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
}
Réponse aux commentaires Pour ceux qui veulent voir comment un sémaphore peut être éliminé sans Task.WaitAll
Exécuter en dessous du code dans l'application console et cette exception sera déclenchée.
System.ObjectDisposedException: 'Le sémaphore a été supprimé.'
static void Main(string[] args)
{
int maxConcurrency = 5;
List<string> messages = Enumerable.Range(1, 15).Select(e => e.ToString()).ToList();
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach (var msg in messages)
{
concurrencySemaphore.Wait();
var t = Task.Factory.StartNew(() =>
{
try
{
Process(msg);
}
finally
{
concurrencySemaphore.Release();
}
});
tasks.Add(t);
}
// Task.WaitAll(tasks.ToArray());
}
Console.WriteLine("Exited using block");
Console.ReadKey();
}
private static void Process(string msg)
{
Thread.Sleep(2000);
Console.WriteLine(msg);
}
Vous pouvez utiliser Parallel.Foreach
et vous appuyer sur MaxDegreeOfParallelism
à la place.
Parallel.ForEach(messages, new ParallelOptions {MaxDegreeOfParallelism = 10},
msg =>
{
// logic
Process(msg);
});
penser serait mieux pour utiliser Parallel LINQ
Parallel.ForEach(messages ,
new ParallelOptions{MaxDegreeOfParallelism = 4},
x => Process(x);
);
où x est le degré maximal de parallélisme
Vous pouvez simplement définir le degré de concurrence maximum comme suit:
int maxConcurrency=10;
var messages = new List<1000>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
foreach(var msg in messages)
{
Task.Factory.StartNew(() =>
{
concurrencySemaphore.Wait();
try
{
Process(msg);
}
finally
{
concurrencySemaphore.Release();
}
});
}
}
Si vous avez besoin d'une mise en file d'attente dans l'ordre (le traitement peut se terminer dans n'importe quel ordre), un sémaphore n'est pas nécessaire. Démodé si les déclarations fonctionnent bien:
const int maxConcurrency = 5;
List<Task> tasks = new List<Task>();
foreach (var arg in args)
{
var t = Task.Run(() => { Process(arg); } );
tasks.Add(t);
if(tasks.Count >= maxConcurrency)
Task.WaitAny(tasks.ToArray());
}
Task.WaitAll(tasks.ToArray());
Vous pouvez créer votre propre TaskScheduler et remplacer la QueueTask ici.
protected virtual void QueueTask(Task task)
Ensuite, vous pouvez faire ce que vous voulez.
Un exemple ici:
public static void RunTasks(List<NamedTask> importTaskList)
{
List<NamedTask> runningTasks = new List<NamedTask>();
try
{
foreach (NamedTask currentTask in importTaskList)
{
currentTask.Start();
runningTasks.Add(currentTask);
if (runningTasks.Where(x => x.Status == TaskStatus.Running).Count() >= MaxCountImportThread)
{
Task.WaitAny(runningTasks.ToArray());
}
}
Task.WaitAll(runningTasks.ToArray());
}
catch (Exception ex)
{
Log.Fatal("ERROR!", ex);
}
}