减速创造了许多线程对象线程、对象、创造了

2023-09-04 07:47:58 作者:越夜越有神

我做了一些产卵数百个线程的一个项目。所有这些线程处于睡眠状态(它们被锁定在一个监视器对象)。我注意到,如果我增加了沉睡的线程数的程序慢下来很多。的有趣的是,在看任务管理似乎线程的数量越多,越自由是处理器。我已经将问题范围缩小到创建对象。

有人可以解释给我听?

我已经产生了小样本进行测试。这是一个控制台程序。它创建一个线程的每个处理器,并测量它的速度用一个简单的测试(一个新的对象())。不,新的对象()不即时编译程(尝试,如果你不相信我)。主线程显示每个线程的速度。 pressing CTRL-C,该程序会生成50沉睡的线程。慢下来开始只有50个线程。随着约250它在任务管理器非常明显的CPU是不是100%的使用(在我的是82%)。

我试图锁定沉睡线程的三种方法:Thread.CurrentThread.Suspend()(坏,坏的,我知道:-)),一个已经锁定的对象和Thread.sleep代码(超时的锁。无穷大)。一样的。如果我评论该行与新的对象(),我用的Math.sqrt(或无)取代它的问题不在于present。速度不同的线程数变化。 别人可以检查吗?没有人知道哪里是瓶颈?

嗯......你应该在发布模式下进行测试而无需从Visual Studio的启动它。 我使用的是双处理器(无HT)XP SP3。我曾与.NET 3.5和4.0(以测试不同的框架运行时)

测试它

 命名空间TestSpeed
{
    使用系统;
    使用System.Collections.Generic;
    使用的System.Threading;

    类节目
    {
        私人常量长ticksInSec =千万;
        私人常量长ticksInMs = ticksInSec / 1000;
        私人const int的threadsT​​ime = 50;
        私人const int的stackSizeBytes = 256 * 1024;
        私人const int的waitTimeMs = 1000;

        私有静态列表< INT>收集=新的名单,其中,INT>();
        私有静态诠释[] objsCreated;

        静态无效的主要(字串[] args)
        {
            objsCreated =新INT [Environment.ProcessorCount]
            Monitor.Enter(objsCreated);

            的for(int i = 0; I< objsCreated.Length;我++)
            {
                新的Thread(工人)。开始(我);
            }

            INT [] oldCount =新INT [objsCreated.Length]

            日期时间最后= DateTime.UtcNow;

            Console.Clear();

            INT numThreads = 0;
            Console.WriteLine(preSS Ctrl-C来生成{0}的睡眠线程,按Ctrl-Break来结束。,threadsT​​ime);

            Console.CancelKey preSS + =(发件人,E)=>
            {
                如果(e.SpecialKey!= ConsoleSpecialKey.ControlC)
                {
                    返回;
                }

                的for(int i = 0; I< threadsT​​ime;我++)
                {
                    新的Thread(()=>
                    {
                        / *同为所有三个办法,以永远锁定一个线程* /
                        //Thread.CurrentThread.Suspend();
                        //Thread.Sleep(Timeout.Infinite);
                        锁定(objsCreated){}
                    },stackSizeBytes)。开始();

                    Interlocked.Increment(REF numThreads);
                }

                e.Cancel =真;
            };

            而(真)
            {
                Thread.sleep代码(waitTimeMs);

                Console.SetCursorPosition(0,1);

                现在的DateTime = DateTime.UtcNow;

                长蜱=(现在 - 过去).Ticks;

                Console.WriteLine(睡了{0}毫秒,蜱/ ticksInMs);

                Thread.MemoryBarrier();

                的for(int i = 0; I< objsCreated.Length;我++)
                {
                    诠释计数= objsCreated [I]
                    Console.WriteLine({0} {1}主题]:{2} /秒,我,numThreads,((长)(计数 -  oldCount [I]))* ticksInSec /蜱);
                    oldCount [我] =计数;
                }

                Console.WriteLine();

                CheckCollects();

                最后=现在;
            }
        }

        私有静态无效工人(obj对象)
        {
            INT九=(INT)目标文件;

            而(真)
            {
                / *第一和第二是通过线,第三,第四,第五和无中生有慢不* /

                新的对象();
                //如果(新的对象()等于(空)。)回报;
                //Math.Sqrt(objsCreated[ix]);
                //如果(的Math.sqrt(objsCreated [九])℃的)回报;
                //Interlocked.Add(ref objsCreated [IX],0);

                Interlocked.Increment(REF objsCreated [九]);
            }
        }

        私有静态无效CheckCollects()
        {
            INT newMax = GC.MaxGeneration;

            而(newMax> collects.Count)
            {
                collects.Add(0);
            }

            的for(int i = 0; I< collects.Count;我++)
            {
                INT NEWCOL = GC.CollectionCount(ⅰ);

                如果(NEWCOL!=收集[I])
                {
                    收集[我] = NEWCOL;
                    Console.WriteLine(收集根{0} {1},我,NEWCOL);
                }
            }
        }
    }
}
 

解决方案

我的猜到的是,问题是,垃圾收集需要一定的线程之间的合作 - 这要么需要检查他们再全部停航,或要求其停止自己,等待它的出现,等等。(即使他们的是的悬挂,它必须告诉他们不要醒来!)

这当然说明了停止世界垃圾收集器。我相信有围绕并行的细节有所不同,至少两种或三种不同的GC的实现......但我怀疑所有的人都将拥有的部分的工作要做,让线程方面合作。

Java多线程 线程创建的三种方式

I'm doing a project that spawn some hundreds of threads. All these threads are in a "sleeping" condition (they are locked on a Monitor object). I have noticed that if I increase the number of "sleeping" threads the program slow down very much. The "funny" thing is that looking at the Task Manager it seems that the greater the number of threads, the more free is the processor. I have narrowed the problem to object creation.

Can someone explain it to me?

I have produced a small sample to test it. It's a console program. It creates a thread for each processor and measure it's speed with a simple test (a "new Object()" ). No, the "new Object()" isn't jitted away (try if you don't trust me). The main thread show the speed of each thread. Pressing CTRL-C, the program spawns 50 "sleeping" threads. The slow down begins with just 50 threads. With around 250 it's very visible on the Task Manager that the CPU isn't 100% used (on mine it's 82%).

I have tried three methods of locking the "sleeping" thread: Thread.CurrentThread.Suspend() (bad, bad, I know :-) ), a lock on an already locked object and a Thread.Sleep(Timeout.Infinite). It's the same. If I comment the row with the new Object(), and I replace it with a Math.Sqrt (or with nothing) the problem isn't present. The speed doesn't change with the number of threads. Can someone else check it? Does anyone knows where is the bottle neck?

Ah... you should test it in Release Mode WITHOUT launching it from the Visual Studio. I'm using XP sp3 on a dual processor (no HT). I have tested it with the .NET 3.5 and 4.0 (to test the different framework 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);
                }
            }
        }
    }
}

解决方案

My guess is that the problem is that garbage collection requires a certain amount of cooperation between threads - something either needs to check that they're all suspended, or ask them to suspend themselves and wait for it to happen, etc. (And even if they are suspended, it has to tell them not to wake up!)

This describes a "stop the world" garbage collector, of course. I believe there are at least two or three different GC implementations which differ in the details around parallelism... but I suspect that all of them are going to have some work to do in terms of getting threads to cooperate.