나는 수백 개의 스레드를 생성하는 프로젝트를 수행하고 있습니다. 이러한 모든 스레드는 "절전 모드"상태입니다 (모니터 개체에 잠김). 나는 "sleeping"쓰레드의 수가 증가하면 프로그램의 속도가 느려진다는 것을 알았다. "재미있는"것은 작업 관리자를 보면 스레드 수가 많을수록 프로세서가 더 많이 사용되는 것처럼 보입니다. 객체 생성에 대한 문제를 좁혔습니다.많은 스레드가있는 개체 생성 속도 저하
누군가 나에게 설명해 줄 수 있습니까?
나는 그것을 테스트하기 위해 작은 샘플을 만들었습니다. 그것은 콘솔 프로그램입니다. 그것은 각 프로세서에 대한 스레드를 생성하고 간단한 테스트로 속도를 측정합니다 ("새 Object()"). 아니요, "새로운 객체()"는 떨어져 나가지 않습니다 (당신이 나를 믿지 않으면 시도하십시오). 주 스레드는 각 스레드의 속도를 보여줍니다. CTRL-C를 누르면이 프로그램은 50 개의 "휴면"스레드를 생성합니다. 속도 저하는 단지 50 개의 스레드로 시작됩니다. 약 250 개는 CPU가 100 % 사용되지 않은 작업 관리자에서 매우 잘 보입니다 (내 경우는 82 %입니다).
"잠자기"스레드를 잠그는 세 가지 방법을 시도했습니다. Thread.CurrentThread.Suspend() (나쁜, 나쁨, 나는 알고 있습니다 :-)), 이미 잠긴 개체와 Thread.Sleep (Timeout . 무한대). 그것은 동일합니다. 새 Object()를 사용하여 행에 주석을 추가하고 Math.Sqrt (또는 아무것도 사용하지 않음)로 바꾸면 문제가 없습니다. 속도는 스레드 수에 따라 변하지 않습니다. 다른 사람이 확인할 수 있습니까? 병목이 어디 있는지 아는 사람 있습니까?
아 ... 비주얼 스튜디오에서 시작하지 않고 릴리스 모드에서 테스트해야합니다. 이중 프로세서 (HT 없음)에서 XP sp3을 사용하고 있습니다. 뭔가 하나 필요 - 나는
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);
}
}
}
}
}
성능에 대해 우려하는 경우 (cpucount) 개 이상의 스레드가 없어야합니다. (cpucount + 2)와 (cpucount * 2) 사이에는 좋은 규칙이 있습니다 (시스템에서 둘 다 4로 나옵니다). 비동기 I/O 조작의 대기열을 사용하여 잠자는 대신 소수의 스레드를 사용중인 상태로 유지하십시오. 스레드가 대기해야하는 유일한 시간은 잠금을 요구할 때입니다. –
"슬로우 모션"코 루틴을하고 있습니다. 스레드 사이의 "전환 시간"은 관련이 없으므로 스레드를 사용할 수 있습니다 (이전의 스레드와 새 스레드간에 전환을 수행하기 위해 일부 ms가 손실 되더라도 초당 "전환"/ 초가 있습니다). 모든 문제). 프로세서와 동일한 수의 스레드가 항상 실행되지만 수면 스레드가 모든 것을 느리게하면 문제가 발생합니다. 아니요, MS의 비동기 라이브러리를 사용할 수 없습니다. 왜냐하면 "가짜"이기 때문입니다. 프로그램을 "다시 작성"합니다. 기존 라이브러리를 사용해야합니다. – xanatos
명시 적으로 쓰레드를 생성하는 대신 TPL을 사용하는 것이 고려 되었습니까? 그렇게하면 프레임 워크가 가장 적절한 수의 원시 스레드를 결정하여 작업을 수행 할 수 있습니다. –