4

并不是说我不欣赏多线程ThreadPoolThreadPool. 所以我在这里提交我的代码,让比我聪明得多的人撕毁。

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

编辑:回答我自己的问题,不,这是坏的:它似乎运行了多次,但在同一个触发器上。这是因为 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) { }
}
4

3 回答 3

4

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

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

编辑

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

于 2009-07-20T00:04:07.123 回答
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);
        });
于 2009-07-19T23:56:32.957 回答
0

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

于 2009-07-19T23:54:38.013 回答