【问题标题】:ThreadPool giving amazing results, did I do this right? (no, I didn't)ThreadPool 给出了惊人的结果,我做对了吗? (不,我没有)
【发布时间】:2010-11-12 04:18:07
【问题描述】:

并不是说我不欣赏多线程或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) { }
}

【问题讨论】:

    标签: c# multithreading threadpool


    【解决方案1】:

    线程池会自动将运行线程的数量限制为最大效率的处理器数量。每个上下文切换最多 1Mb(默认)以 4Kb 页面增量的内存交换,因此如果您使用的线程比内核多得多,您可能会得到一个无需上下文切换即可获得大量速度。

    【讨论】:

    • 上下文切换不会导致 1 MB 的“内存交换”。默认情况下,Win32 堆栈是 1 MB 的虚拟内存,但大多数线程永远不会使用那么多。此外,当上下文切换确实发生时,唯一与堆栈相关的更改是ESP 寄存器现在指向新线程的堆栈。无需将内存实际移动到任何地方。
    • 除非上下文切换当然涉及分页(这在我当前的开发机器上是一个真正的问题,只有 256MB 内存),但这完全是另一个问题......
    • 它只会分页出实际使用的页面,这意味着以 4k 为增量,而不是 1M,所以它并不像听起来那么糟糕。话虽如此,上下文切换的开销永远不会是微不足道的,所以最好不要有更多的线程,而不是让你真正受益。这个数字将取决于很多因素,并且可能最好通过反复试验来确定。
    • 感谢先生们的澄清。我想我只知道上吊自己=)
    • 你修好了,所以我否定了你的否定。
    【解决方案2】:

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

    更好的办法是让每个线程检查它是否达到零,如果达到则设置一个事件。然后,您可以等待事件而不是轮询。唯一的技巧是您希望在调度循环之前在主线程中增加一次,然后在等待事件之前减少并检查零。

    编辑

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

    【讨论】:

    • 同意。我通过将t 作为状态变量传递给 ThreadPool 来修复它,然后我们就走了。
    • 另外,不能在函数局部变量上设置 volatile。 Interlocked.Equals?
    • 仍然比没有 ThreadPool 好很多,但我还没有做任何真正的措施
    • 我同意史蒂夫的观点:轮询循环是一个错误,您确实需要一个 EventWaitHandle 来让线程发出信号。
    • 啊,好的。如果您不使用池,也可以将线程设置为背景。
    【解决方案3】:

    我认为你可以做得更好。无需锁定对 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);
            });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-30
      • 1970-01-01
      相关资源
      最近更新 更多