Angesichts dieses Codes:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
Werden alle 1000 Threads fast gleichzeitig erscheinen?
Angesichts dieses Codes:
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
DoSomething(someString);
});
Werden alle 1000 Threads fast gleichzeitig erscheinen?
Antworten:
Nein, es werden nicht 1000 Threads gestartet - ja, es wird die Anzahl der verwendeten Threads begrenzt. Parallele Erweiterungen verwenden eine angemessene Anzahl von Kernen, je nachdem, wie viele Sie physisch haben und wie viele bereits beschäftigt sind. Es weist jedem Kern Arbeit zu und verwendet dann eine Technik namens Arbeitsdiebstahl, damit jeder Thread seine eigene Warteschlange effizient verarbeiten kann und nur dann teuren Cross-Thread-Zugriff ausführen muss, wenn dies wirklich erforderlich ist.
Werfen Sie einen Blick auf die PFX Team Blog für Lasten von Informationen darüber , wie es funktioniert und alle Arten von anderen Themen zuordnet.
Beachten Sie, dass Sie in einigen Fällen auch den gewünschten Parallelitätsgrad angeben können.
Auf einem Single-Core-Computer ... Parallel.ForEach-Partitionen (Chunks) der Sammlung, an denen zwischen einer Reihe von Threads gearbeitet wird. Diese Anzahl wird jedoch auf der Grundlage eines Algorithmus berechnet, der die von der Threads, die dem ForEach zugewiesen werden. Also , wenn der Körperteil des ForEach ruft zu lange laufen IO-bound / Sperrfunktionen , die den Faden herum warten lassen würden, wird der Algorithmus mehr Threads erzeugen und neu partitionieren die Sammlung zwischen ihnen . Wenn die Threads schnell abgeschlossen sind und beispielsweise keine E / A-Threads blockieren, z. B. einfach einige Zahlen berechnen,Der Algorithmus erhöht (oder verringert) die Anzahl der Threads bis zu einem Punkt, an dem der Algorithmus das Optimum für den Durchsatz berücksichtigt (durchschnittliche Abschlusszeit jeder Iteration) .
Grundsätzlich ermittelt der Thread-Pool hinter all den verschiedenen Funktionen der parallelen Bibliothek eine optimale Anzahl von zu verwendenden Threads. Die Anzahl der physischen Prozessorkerne bildet nur einen Teil der Gleichung. Es gibt KEINE einfache Eins-zu-Eins-Beziehung zwischen der Anzahl der Kerne und der Anzahl der erzeugten Threads.
Ich finde die Dokumentation zum Abbrechen und Behandeln von Synchronisierungsthreads nicht sehr hilfreich. Hoffentlich kann MS bessere Beispiele in MSDN liefern.
Vergessen Sie nicht, dass der Body-Code so geschrieben sein muss, dass er auf mehreren Threads ausgeführt werden kann, zusammen mit allen üblichen Überlegungen zur Thread-Sicherheit. Das Framework abstrahiert diesen Faktor noch nicht.
Basierend auf der Anzahl der Prozessoren / Kerne wird eine optimale Anzahl von Threads ermittelt. Sie werden nicht alle auf einmal erscheinen.
Siehe Verwendet Parallel.Für die Verwendung einer Aufgabe pro Iteration? für eine Idee eines "mentalen Modells" zu verwenden. Der Autor erklärt jedoch: "Letztendlich ist es wichtig, sich daran zu erinnern, dass sich die Implementierungsdetails jederzeit ändern können."
Gute Frage. In Ihrem Beispiel ist der Parallelisierungsgrad selbst auf einem Quad-Core-Prozessor ziemlich niedrig, aber mit einigen Wartezeiten kann der Parallelisierungsgrad ziemlich hoch werden.
// Max concurrency: 5
[Test]
public void Memory_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Schauen Sie sich nun an, was passiert, wenn ein Wartevorgang hinzugefügt wird, um eine HTTP-Anforderung zu simulieren.
// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Ich habe noch keine Änderungen vorgenommen und der Grad der Parallelität / Parallelisierung ist dramatisch gestiegen. Das Limit der Parallelität kann mit erhöht werden ParallelOptions.MaxDegreeOfParallelism
.
// Max concurrency: 43
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(1000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
// Max concurrency: 391
[Test]
public void Test()
{
ConcurrentBag<int> monitor = new ConcurrentBag<int>();
ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(monitor.Count);
System.Threading.Thread.Sleep(100000);
monitor.TryTake(out int result);
monitorOut.Add(result);
});
Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}
Ich empfehle die Einstellung ParallelOptions.MaxDegreeOfParallelism
. Dies erhöht nicht unbedingt die Anzahl der verwendeten Threads, stellt jedoch sicher, dass Sie nur eine vernünftige Anzahl von Threads starten, was Ihr Anliegen zu sein scheint.
Um Ihre Frage zu beantworten: Nein, Sie werden nicht alle Threads gleichzeitig starten. Verwenden Sie Parallel.Invoke, wenn Sie perfekt parallel aufrufen möchten, z. B. um die Rennbedingungen zu testen.
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
ConcurrentBag<string> monitor = new ConcurrentBag<string>();
ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
var arrayStrings = new string[1000];
var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
Parallel.ForEach<string>(arrayStrings, options, someString =>
{
monitor.Add(DateTime.UtcNow.Ticks.ToString());
monitor.TryTake(out string result);
monitorOut.Add(result);
});
var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}