ThreadPool дает потрясающие результаты, я сделал это правильно? (нет, я не сделал) - PullRequest
4 голосов
/ 20 июля 2009

Не то, чтобы я не ценил способности многопоточности или ThreadPool, но я боюсь, что что-то сломал, так как я получаю примерно 20-кратное увеличение скорости (на 2-3 секунды меньше, чем за минуту) с относительно наивное использование ThreadPool. Поэтому я отправляю свой код сюда, чтобы его разлучили люди, намного мудрее меня.

Я что-то здесь не так делаю, или это просто лучший кандидат на многопоточность, чем я когда-либо надеялся? (Да, эта функция является целым потоком: как я уже говорил, раньше на ее выполнение уходило более минуты)

РЕДАКТИРОВАТЬ: Чтобы ответить на мой собственный вопрос, нет, это не работает: Кажется, он работает несколько раз, но через один и тот же триггер. Это из-за способа обработки лямбды?

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 ]

4 голосов
/ 20 июля 2009

Interlocked.Increment лучше, чем блокировка, но цикл опроса в конце меня пугает. Во-первых, если вы собираетесь выполнить цикл, то выполните Thread.Sleep (0), чтобы каждый раз освобождать процессор. Во-вторых, если вы собираетесь опрашивать переменную, вам нужно убедиться, что она помечена как volatile или вы используете MemoryBarrier, иначе компилятор может предположить, что никакой внешний поток не изменит ее, и поэтому оптимизирует проверку, что приведет к бесконечности цикл. * * 1 001

Еще лучше было бы, чтобы каждый поток проверял, что он достигает нуля, и устанавливает событие, если это так. Затем вы можете ждать события вместо опроса. Единственная хитрость заключается в том, что вы хотите один раз увеличить в главном потоке перед циклом отправки, затем уменьшить и проверить на ноль перед ожиданием события.

редактировать

Если он сломан из-за повторного использования триггера, значит, закрытие неверно. Попытка копирования значения world в локальную переменную внутри цикла и использование этой переменной для лямбда-выражения.

1 голос
/ 20 июля 2009

Я думаю, вы можете сделать лучше. Нет необходимости блокировать изменения в 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);
        });
0 голосов
/ 20 июля 2009

Ну, ThreadPool автоматически ограничивает количество запущенных потоков числом процессоров, что максимально эффективно. Каждый переключатель контекста имеет значение до 1 МБ (по умолчанию) при увеличении страницы на 4 КБ перестановки памяти, поэтому, если вы используете намного больше потоков, чем ядер, вы можете получить тонну Скорость просто без переключения контекста.

...