ThreadPool给出了惊人的结果,我做到了吗? (不,我没有)

时间:2009-07-19 23:37:58

标签: c# multithreading threadpool

并不是说我不太欣赏多线程或ThreadPool的权力,但是我害怕因为速度增加大约20倍(从一分钟以来减少2-3次)而打破了一些东西相对天真地使用ThreadPool。所以我在这里提交我的代码,被比我更聪明的人撕裂。

我在这里做错了什么,或者这是一个比我希望的更好的多线程候选者? (是的,这个函数是一个完整的线程:就像我说的,这曾经花了一分钟才能运行)

编辑:要回答我自己的问题,不,这已经破了:它似乎在多次运行,但是在同一个触发器上运行。这是因为处理lambda的方式吗?

private static void CompileEverything()
{
    try
    {
        // maintain the state of our systray icon
        object iconLock = new object();
        bool iconIsOut = true;

        // keep a count of how many threads are still running
        object runCountLock = new object();
        int threadRunning = 0;

        foreach (World w in Worlds)
        {
            foreach (Trigger t in w.Triggers)
            {
                lock (runCountLock)
                {
                    threadRunning++;
                }

                ThreadPool.QueueUserWorkItem(o =>
                {
                    // [snip]: Do some work involving compiling code already in memory with CSharpCodeProvider

                    // provide some pretty feedback
                    lock (iconLock)
                    {
                        if (iconIsOut)
                            notifyIcon.Icon = Properties.Resources.Icon16in;
                        else
                            notifyIcon.Icon = Properties.Resources.Icon16out;

                        iconIsOut = !iconIsOut;
                    }

                    lock (runCountLock)
                    {
                        threadRunning--;
                    }
                });
            }
        }

        // wait for all the threads to finish up
        while (true)
        {
            lock (runCountLock)
            {
                if (threadRunning == 0)
                    break;
            }
        }

        // set the notification icon to our default icon.
        notifyIcon.Icon = Properties.Resources.Icon16;
    }
    // we're going down before we finished starting...
    // oh well, be nice about it.
    catch (ThreadAbortException) { }
}

3 个答案:

答案 0 :(得分:4)

Interlocked.Increment比锁定更好,但最后的轮询循环让我害怕。首先,如果你要循环,那么每次都要执行Thread.Sleep(0)来释放处理器。其次,如果你要轮询一个变量,那么你需要确保它标记为volatile或你使用MemoryBarrier,否则编译器可能会假设没有外部线程会改变它,因此优化掉检查,导致无限循环。

更好的是每个线程检查它是否达到零并设置事件(如果有)。然后,您可以等待事件而不是轮询。唯一的技巧是你想在调度循环之前在主线程中递增一次,然后在等待事件之前递减并检查零。

修改

如果它因为重复使用触发器而被破坏,那么关闭是错误的。尝试将world的值复制到循环内部的局部变量中,并将该变量用于lambda表达式。

答案 1 :(得分:1)

我认为你可以做得更好。无需锁定对threadRunning的更改。你可以使用Interlocked.Increment()和Interlocked.Decrement():

        Interlocked.Increment(ref threadRunning);
        ThreadPool.QueueUserWorkItem(o =>
        {
            // [snip]: Do some work involving compiling code already in memory with CSharpCodeProvider

            // provide some pretty feedback
            lock (iconLock)
            {
                notifyIcon.Icon = (iconIsOut ? Properties.Resources.Icon16in : Properties.Resources.Icon16out);
                iconIsOut = !iconIsOut;
            }

            Interlocked.Decrement(ref threadRunning);
        });

答案 2 :(得分:0)

ThreadPool自动将运行线程的数量限制为最高效的处理器数量。每个上下文切换最多 1Mb(默认)以4Kb页为增量进行内存交换,因此如果您使用的内核线程比内核多得多,那么您可能会得到一个没有上下文切换的速度很快。