, я делаю проект, который порождает несколько сотен потоков. Все эти потоки находятся в состоянии" сна "(они заблокированы на объекте Monitor). Я заметил, что если я увеличиваю количество «спящих» потоков, программа сильно замедляется. "Забавно" то, что при взгляде на диспетчер задач кажется, что чем больше потоков, тем свободнее процессор. Я сузил проблему до создания объекта.
Кто-нибудь может мне это объяснить?
Я изготовил небольшой образец для проверки. Это консольная программа. Он создает поток для каждого процессора и измеряет его скорость с помощью простого теста («новый объект ()»). Нет, «новый объект ()» не убирается (попробуйте, если вы мне не доверяете). Основной поток показывает скорость каждого потока. При нажатии CTRL-C программа порождает 50 «спящих» потоков. Замедление начинается всего с 50 потоков. Около 250 шт. В диспетчере задач очень заметно, что ЦП используется не на 100% (в моем случае это 82%).
Я пробовал три метода блокировки «спящего» потока: Thread.CurrentThread.Suspend () (плохо, плохо, я знаю :-)), блокировку уже заблокированного объекта и Thread.Sleep (Timeout. Бесконечный). Это то же самое. Если я прокомментирую строку новым Object () и заменю ее на Math.Sqrt (или ничего), проблемы не будет. Скорость не меняется с количеством потоков. Sqrt (или ни с чем) проблемы нет. Скорость не меняется с количеством потоков. Sqrt (или ни с чем) проблемы нет. Скорость не меняется с количеством потоков. Может кто нибудь еще это проверить? Кто-нибудь знает, где находится бутылочное горлышко?
А ... вы должны протестировать его в режиме выпуска БЕЗ запуска из Visual Studio. Я использую XP sp3 на двухпроцессорном (без HT). Я тестировал его с .NET 3.5 и 4.0 (для тестирования различных сред выполнения)
namespace TestSpeed
{
using System;
using System.Collections.Generic;
using System.Threading;
class Program
{
private const long ticksInSec = 10000000;
private const long ticksInMs = ticksInSec / 1000;
private const int threadsTime = 50;
private const int stackSizeBytes = 256 * 1024;
private const int waitTimeMs = 1000;
private static List<int> collects = new List<int>();
private static int[] objsCreated;
static void Main(string[] args)
{
objsCreated = new int[Environment.ProcessorCount];
Monitor.Enter(objsCreated);
for (int i = 0; i < objsCreated.Length; i++)
{
new Thread(Worker).Start(i);
}
int[] oldCount = new int[objsCreated.Length];
DateTime last = DateTime.UtcNow;
Console.Clear();
int numThreads = 0;
Console.WriteLine("Press Ctrl-C to generate {0} sleeping threads, Ctrl-Break to end.", threadsTime);
Console.CancelKeyPress += (sender, e) =>
{
if (e.SpecialKey != ConsoleSpecialKey.ControlC)
{
return;
}
for (int i = 0; i < threadsTime; i++)
{
new Thread(() =>
{
/* The same for all the three "ways" to lock forever a thread */
//Thread.CurrentThread.Suspend();
//Thread.Sleep(Timeout.Infinite);
lock (objsCreated) { }
}, stackSizeBytes).Start();
Interlocked.Increment(ref numThreads);
}
e.Cancel = true;
};
while (true)
{
Thread.Sleep(waitTimeMs);
Console.SetCursorPosition(0, 1);
DateTime now = DateTime.UtcNow;
long ticks = (now - last).Ticks;
Console.WriteLine("Slept for {0}ms", ticks / ticksInMs);
Thread.MemoryBarrier();
for (int i = 0; i < objsCreated.Length; i++)
{
int count = objsCreated[i];
Console.WriteLine("{0} [{1} Threads]: {2}/sec ", i, numThreads, ((long)(count - oldCount[i])) * ticksInSec / ticks);
oldCount[i] = count;
}
Console.WriteLine();
CheckCollects();
last = now;
}
}
private static void Worker(object obj)
{
int ix = (int)obj;
while (true)
{
/* First and second are slowed by threads, third, fourth, fifth and "nothing" aren't*/
new Object();
//if (new Object().Equals(null)) return;
//Math.Sqrt(objsCreated[ix]);
//if (Math.Sqrt(objsCreated[ix]) < 0) return;
//Interlocked.Add(ref objsCreated[ix], 0);
Interlocked.Increment(ref objsCreated[ix]);
}
}
private static void CheckCollects()
{
int newMax = GC.MaxGeneration;
while (newMax > collects.Count)
{
collects.Add(0);
}
for (int i = 0; i < collects.Count; i++)
{
int newCol = GC.CollectionCount(i);
if (newCol != collects[i])
{
collects[i] = newCol;
Console.WriteLine("Collect gen {0}: {1}", i, newCol);
}
}
}
}
}