मैं एक परियोजना कर रहा हूं जो कुछ सैकड़ों धागे पैदा करता है। ये सभी धागे "नींद" स्थिति में हैं (वे मॉनीटर ऑब्जेक्ट पर बंद हैं)। मैंने देखा है कि अगर मैं "सोने" धागे की संख्या में वृद्धि करता हूं तो कार्यक्रम बहुत धीमा हो जाता है। "हास्यास्पद" बात यह है कि कार्य प्रबंधक को देखते हुए ऐसा लगता है कि धागे की संख्या जितनी अधिक होगी, प्रोसेसर जितना अधिक होगा। मैंने निर्माण को ऑब्जेक्ट करने के लिए समस्या को संकुचित कर दिया है।कई धागे के साथ वस्तुओं को बनाने पर मंदी
क्या कोई मुझे यह समझा सकता है?
मैंने इसका परीक्षण करने के लिए एक छोटा सा नमूना तैयार किया है। यह एक कंसोल कार्यक्रम है। यह प्रत्येक प्रोसेसर के लिए एक धागा बनाता है और इसकी गति को एक साधारण परीक्षण (एक "नया ऑब्जेक्ट()") के साथ मापता है। नहीं, "नया ऑब्जेक्ट()" दूर नहीं है (अगर आप मुझ पर भरोसा नहीं करते हैं तो कोशिश करें)। मुख्य धागा प्रत्येक धागे की गति दिखाता है। CTRL-C दबाकर, प्रोग्राम 50 "सोने" धागे पैदा करता है। धीमी गति केवल 50 धागे से शुरू होती है। लगभग 250 के साथ यह कार्य प्रबंधक पर बहुत दिखाई देता है कि सीपीयू 100% उपयोग नहीं किया जाता है (मेरे 82% पर)।
मैं "सो" धागा लॉकिंग की तीन तरीकों की कोशिश की है: Thread.CurrentThread.Suspend() (बुरा, बुरा, मुझे पता है :-)), एक पहले से ही बंद कर दिया वस्तु पर एक ताला और एक Thread.Sleep (समय समाप्त .Infinite)। यह ऐसा ही है। यदि मैं नई ऑब्जेक्ट() के साथ पंक्ति पर टिप्पणी करता हूं, और मैं इसे Math.qrt (या कुछ भी नहीं) के साथ प्रतिस्थापित करता हूं तो समस्या मौजूद नहीं है। गति धागे की संख्या के साथ बदल नहीं है। क्या कोई और इसे देख सकता है? क्या कोई जानता है कि बोतल की गर्दन कहां है?
आह ... आपको इसे विजुअल स्टूडियो से लॉन्च किए बिना रिलीज मोड में परीक्षण करना चाहिए। मैं एक दोहरी प्रोसेसर (कोई एचटी) पर XP SP3 का उपयोग कर रहा हूं। कुछ या तो की जरूरत है - मैं (अलग ढांचा runtimes परीक्षण करने के लिए)
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 संचालन की कतारों का उपयोग करें। लॉक के लिए दंडित होने पर केवल एक थ्रेड का इंतजार करना चाहिए। –
मैं एक "धीमी गति" coroutines कर रहा हूँ। धागे के बीच "स्विच टाइम" अप्रासंगिक है, इसलिए मैं धागे का उपयोग कर सकता हूं (मेरे पास "स्विच"/सेकेंड है, इसलिए यदि यह पुराने धागे और नए थ्रेड के बीच स्विच करने के लिए कुछ एमएस खो देता है तो मेरे पास नहीं है कोई परेशानी)। प्रोसेसर के बराबर चलने वाले कई धागे हमेशा होते हैं, लेकिन यदि सोते धागे सबकुछ धीमा करते हैं, तो मुझे एक समस्या है। नहीं, मैं एमएस की एसिंक लाइब्रेरी का उपयोग नहीं कर सकता, क्योंकि यह "नकली" है। यह आपके कार्यक्रम को "पुनः लिखता है"। मुझे कुछ पूर्ववर्ती पुस्तकालयों का उपयोग करना है। – xanatos
क्या आपने स्पष्ट रूप से धागे बनाने के बजाय टीपीएल का उपयोग करने पर विचार किया है? इस तरह ढांचा काम करने के लिए देशी धागे की सबसे उचित संख्या तय कर सकता है। –