Відповіді:
Ні, він не запустить 1000 ниток - так, це обмежить кількість потоків. Паралельне розширення використовує відповідну кількість ядер, виходячи з того, скільки фізично у вас є і скільки вже зайнято. Він виділяє роботу для кожного ядра, а потім використовує техніку, яку називають роботою крадіжки, щоб дозволити кожному потоку ефективно обробляти власну чергу і потрібно робити будь-який дорогий доступ між перехресними потоками, коли це дійсно потрібно.
Перегляньте блог команди PFX, щоб отримати багато інформації про те, як він розподіляє роботу та всі інші теми.
Зауважте, що в деяких випадках ви також можете вказати бажану ступінь паралелізму.
На одній основній машині ... Parallel.ForEach розділів (фрагментів) колекції, над якими вона працює, між кількома потоками, але це число розраховується на основі алгоритму, який враховує і, здається, постійно контролює роботу, виконану нитки, які він виділяє ForEach. Отже, якщо частина тіла ForEach закликає до тривалого функціонування / блокування IO, що дозволить потоку чекати навколо, алгоритм породжує більше потоків і перерозподіляє колекцію між ними . Якщо потоки швидко завершуються і не блокуються, наприклад, в потоках вводу-виводу, наприклад, просто обчислюючи деякі числа,алгоритм збільшить (або справді зменшить) кількість потоків до точки, в якій алгоритм вважає оптимальним для пропускної здатності (середній час завершення кожної ітерації) .
В основному пул потоків, що знаходиться за всіма різними функціями бібліотеки Паралельних, створить оптимальну кількість потоків для використання. Кількість фізичних ядер процесора складає лише частину рівняння. Немає простого відношення між кількістю ядер та кількістю породжених ниток.
Я не вважаю документацію навколо скасування та обробки синхронізованих потоків дуже корисною. Сподіваємось, MS може надати кращі приклади в MSDN.
Не забувайте, код тіла повинен бути написаний для запуску на декілька потоків, а також зі всіма звичними міркуваннями безпеки потоку, рамки не абстрагують цей фактор ... поки що.
Він розробляє оптимальну кількість потоків на основі кількості процесорів / ядер. Вони не всі будуть нерестувати відразу.
Див. Чи паралельно.Для використання одного завдання на ітерацію? для використання ідеї "ментальної моделі". Однак автор констатує, що "Зрештою, важливо пам’ятати, що деталі щодо впровадження можуть змінюватися в будь-який час".
Чудове запитання. У вашому прикладі рівень паралелізації досить низький навіть на чотирьохядерному процесорі, але при певному очікуванні рівень паралелізації може стати досить високим.
// 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());
}
Тепер подивіться, що відбувається, коли для моделювання запиту HTTP додано операцію очікування.
// 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());
}
Я ще не вніс жодних змін, і рівень одночасності / паралельності різко підскочив. Паралельність може збільшувати ліміт 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());
}
Я рекомендую налаштування ParallelOptions.MaxDegreeOfParallelism
. Це не обов'язково збільшуватиме кількість потоків, що використовуються, але це забезпечить лише запуску здорової кількості потоків, що, здається, викликає занепокоєння.
І нарешті, щоб відповісти на ваше запитання, ні, ви не отримаєте відразу всі теми. Використовуйте Parallel.Invoke, якщо ви хочете паралельно викликати, наприклад, тестуючи умови гонки.
// 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)));
}