Спорадические проблемы при запуске многопоточного Java-проекта в Win7 - PullRequest
12 голосов
/ 19 декабря 2011

Я работаю над проектом, который требует много памяти и вычислительных ресурсов. Значительная часть выполнения использует многопоточность на FixedThreadPool. Короче; У меня есть поток 1 для извлечения данных из нескольких удаленных местоположений (с использованием URL-соединений) и заполнения BlockingQueue объектами для анализа и потоками n , которые выбирают эти объекты и запускают анализ , изменить: см. Код ниже

Теперь эта установка работает как чудо на моей машине с Linux, работающей с OpenSUSE 11.3, но коллега, тестирующий ее на очень похожей машине с Win7, получает пользовательских уведомлений о тайм-аутах при опросе очереди (см. Код ниже), их много на самом деле. Я пытался отслеживать использование процессора на ее компьютере, и кажется, что программное обеспечение не получает больше 15% процессоров, в то время как на моей машине загрузка процессора достигает предела, как я и предполагал.

Тогда мой вопрос: может ли это быть признаком "голодания" очереди? Может ли быть так, что поток производителя не получает достаточно процессорного времени? Если да, как мне дать одному конкретному потоку в пуле более высокий приоритет?

UPDATE: Я пытался определить проблему, не испытывая радости ... Я, однако, получил некоторые новые идеи.

  • Профилирование выполнения кода с помощью JVisualVM демонстрирует очень своеобразное поведение. Методы вызываются короткими пакетами процессорного времени с интервалом в несколько секунд без прогресса. Для меня это означает, что каким-то образом ОС тормозит процесс.

  • Отключение антивируса и резервных демонов не оказывает существенного влияния на этот вопрос

  • Изменение приоритета java.exe (единственный экземпляр) через диспетчер задач (рекомендуется здесь ) также ничего не меняет. (Это, как говорится, я не мог дать приоритет в реальном времени для Java, и должен был довольствоваться "высоким" prio)

  • Профилирование использования сети показывает хороший поток данных, входящий и выходящий, поэтому я предполагаю, что это не является узким местом (хотя это значительная часть времени выполнения процесса, но это я уже знаю и почти такой же процент, как у моего компьютера с Linux).

Есть идеи, как ОС Win7 может ограничивать время процессора моим проектом? если это не ОС, что может быть ограничивающим фактором? Я хотел бы еще раз подчеркнуть, что на машине НЕ выполняются никакие другие интенсивные вычисления в одно и то же время, и нагрузка на процессор, кроме моего программного обеспечения, почти отсутствует. Это сводит меня с ума ...

РЕДАКТИРОВАТЬ: соответствующий код

public ConcurrencyService(Dataset d, QueryService qserv, Set<MyObject> s){

    timeout = 3;
    this.qs = qserv;
    this.bq = qs.getQueue();
    this.ds = d;
    this.analyzedObjects = s;
    this.drc = DebugRoutineContainer.getInstance();
    this.started = false;

    int nbrOfProcs = Runtime.getRuntime().availableProcessors();
    poolSize = nbrOfProcs;
    pool = (ThreadPoolExecutor) Executors.newFixedThreadPool(poolSize);
    drc.setScoreLogStream(new PrintStream(qs.getScoreLogFile()));
}

public void serve() throws InterruptedException {
    try {
        this.ds.initDataset();
        this.started = true;
        pool.execute(new QueryingAction(qs));
        for(;;){
            MyObject p = bq.poll(timeout, TimeUnit.MINUTES);

            if(p != null){
                if (p.getId().equals("0"))
                    break;

                pool.submit(new AnalysisAction(ds, p, analyzedObjects, qs.getKnownAssocs()));
            }else 
                drc.log("Timed out while waiting for an object...");

        }

      } catch (Exception ex) {
            ex.printStackTrace();
            String exit_msg = "Unexpected error in core analysis, terminating execution!";

      }finally{
            drc.log("--DEBUG: Termination criteria found, shutdown initiated..");
            drc.getMemoryInfo(true);    // dump meminfo to log

            pool.shutdown();

            int mins = 2;
            int nCores = poolSize;
            long    totalTasks = pool.getTaskCount(), 
                    compTasks = pool.getCompletedTaskCount(),
                    tasksRemaining = totalTasks - compTasks,
                    timeout = mins * tasksRemaining / nCores;

            drc.log("--DEBUG: Shutdown commenced, thread pool will terminate once all objects are processed, " +
                        "or will timeout in : " + timeout + " minutes... \n" + compTasks + " of " +  (totalTasks -1) + 
                        " objects have been analyzed so far, " + "mean process time is: " +
                        drc.getMeanProcTimeAsString() + " milliseconds.");

            pool.awaitTermination(timeout, TimeUnit.MINUTES);
      }

}

Класс QueryingAction - это простой Runnable, который вызывает метод сбора данных в обозначенном объекте QueryService, который затем заполняет BlockingQueue. Класс AnalysisAction выполняет все вычисления чисел для одного экземпляра MyObject.

Ответы [ 8 ]

3 голосов
/ 01 февраля 2012

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

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

Поток потребителя:

while (!terminate)
{
    synchronized (Producer.getLockObject())
    {
        try
        {
            //sleep (no processing at all)
            Producer.getLockObject().wait(); 
        }
        catch (Exceptions..)
    }

    MyObject p = Producer.getObjectFromQueue(); //this function should be synchronized

    //Analyse fetched data, and submit it to somewhere...   
}    

Поток производителя:

while (!terminate)
{
    MyObject newData = fetchData(); //fetch data from remote location

    addDataToQueueu(newData); //this should also be synchronized

    synchronized (getLockObject())
    {
        //wake up one thread to deal with the data
        getLockObject().notify();
    }
}

Вы видите, что таким образом ваши потоки всегда работаютполезная работа или сон.Это всего лишь черновик кода для примера.Более подробное объяснение здесь: http://www.javamex.com/tutorials/wait_notify_how_to.shtml и здесь: http://www.java -samples.com / showtutorial.php? Tutorialid = 306

3 голосов
/ 19 декабря 2011

Я подозреваю, что поток производителя не получает / загружает исходные данные достаточно быстро. Это может быть не недостаток процессора, а проблема с вводом-выводом. (не знаю, почему у вас время ожидания в BlockingQueue)

Возможно, стоит иметь поток, который периодически регистрирует такие вещи, как количество добавленных задач и длину очереди (например, каждые 5-15 секунд)

1 голос
/ 02 февраля 2012

Так что после недель возни, борьбы в коде и других видов страданий, я думаю, у меня был прорыв, «момент ясности», если хотите ...

Мне удалось показать, что программа можетдемонстрирует такое же медленное поведение на моей машине с Linux и действительно может работать на полной скорости на проблемной машине с Win-7.Суть проблемы, по-видимому, заключается в некотором повреждении файлов системы / кэша, которые используются для хранения результатов предыдущих запросов, и в целом для ускорения анализа.Вы должны любить иронию, в этом случае они оказались причиной крайне медленного анализа.Оглядываясь назад, я должен был знать (как бритва Оккама) ...

Я до сих пор не уверен, как происходит повреждение, но, по крайней мере, это, вероятно, не связано с другими ОС.Однако использование системных файлов с моей машины увеличивает производительность на хосте Win7 примерно до 40%.Более подробное профилирование процесса также показало, что, как ни странно, на Win7 значительно больше активности GC, которая, очевидно, заняла много процессорного времени из-за перебора чисел.Giving -Xmx2g заботится о чрезмерной сборке мусора, а загрузка ЦП для процесса возрастает до 95-96%, а потоки работают гладко.

Теперь, когда на мой первоначальный вопрос дан ответ, я должен сказать, что общая отзывчивость java определенно лучше в среде Linux, даже без выделения большей кучи памяти, я могу легко выполнять многозадачные задачи, пока выполняю обширный анализ вфон.В Win-7 дела обстоят не так гладко, но изменение размера графического интерфейса значительно медленнее, когда анализ запускается на полной скорости.

Спасибо за все ответы, прошу прощения за частично вводящее в заблуждение описание проблемы.Я просто поделился тем, что узнал во время отладки в меру своих способностей.В любом случае, я считаю, что награда достается Питеру Лоури, так как он вначале указал на проблему ввода / вывода, и именно его предложение о ветке логгера привело меня к ответу.

1 голос
/ 20 декабря 2011

Когда вы говорите URL-соединение, вы имеете в виду локальный или удаленный? Возможно, скорость сети замедляет вашего производителя

1 голос
/ 19 декабря 2011

Вы можете попытаться отделить поток производителя от пула (т.е. создать отдельный Thread и установить для пула значение -1 текущей емкости), а затем установить его приоритет на максимум с помощью setPriority. Посмотрите, что происходит, хотя приоритет редко учитывает такую ​​разницу в производительности.

1 голос
/ 19 декабря 2011

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

Сколько ядер у машины? Вполне возможно, что поток производителя работает на полной скорости, а ЦП по-прежнему просто не хватает. Также возможно, что производитель связан с вводом / выводом.

0 голосов
/ 21 ноября 2013

Извините, но на самом деле не отвечаю, но не уместился внутри комментария, и все же стоит прочитать, я думаю:

  • ну, я не дружу с JAVA
  • но у меня недавно та же проблема с проектами C ++ для управления машиной через USB.
  • В XP или W2K все отлично работает в течение месяцев работы в режиме 24/7 на любом из двух или более основных компьютеров
  • На W7 и достаточно сильной машине все идет нормально, но иногда (примерно 1 раз в несколько часов) зависает на несколько секунд без видимой причины.
  • На W7 и относительно слабой машине (2-ядерный ноутбук T2300E с тактовой частотой 1,66 ГГц) потоки на некоторое время замирают и снова запускаются, что приводит к переполнению USB / WIN / App FIFO и прерыванию связи ...
    • Похоже, что ничто не заблокировано, но шедулер W7 просто не дает процессору время от времени нужные потоки.
    • я думал, что связь с USB-драйвером (JUNGO) замораживает бутон, что не соответствует действительности, я измерил его, и это нормально даже в заморозке
    • Заморозка составляла около 6-15 секунд примерно раз в минуту.
    • после добавления некоторых безопасных снов к петлям нитей заморозка сократилась до 0,5 с
    • но все еще там
    • , даже если приложение не находится под / переполняет FIFO-файлы со стороны USB-драйвера Windows (несколько раз в минуту в течение нескольких мс)
  • Изменение приоритета exe / threads и класса не влияет на производительность на W7 (на XP W2K работает как надо)

Как видите, у нас, скорее всего, та же проблема. В моем случае:

  • не связан с вводом / выводом (когда я заменяю USB-поток симуляцией устройства, он ведет себя аналогично)
  • очень помогает добавление Sleep в код, критичный ко времени
  • ошибка также присутствует в небольшом количестве потоков [2 быстрых (17 мс) + 1 медленных (250 мс) + код приложения = 4]
  • мой расход процессора на медленной машине W7 также не 100%, а около 95%, что нормально, потому что я сплю везде
  • мои приложения используют около 40-100 МБ памяти, но требуют вычислительных ресурсов процессора ...
    • но не настолько, чтобы он мог безопасно работать на гораздо более медленных машинах
    • но из-за подключения USB-драйвера и поддержки нескольких устройств требуется как минимум 2 ядра
  • Мой следующий шаг - добавить запись / анализ времени выполнения, чтобы увидеть, что происходит более подробно
  • , а также небольшая перезапись потоков отправки / получения, чтобы посмотреть, поможет ли это

Когда я узнаю что-то новое / полезное, я добавлю это.

0 голосов
/ 01 февраля 2012

Я бы подумал, что это какая-то проблема, специфичная для ОС, потому что это основное различие между двумя устройствами. Точнее говоря, что-то замедляет поступление данных через удаленное соединение.

Найдите какой-нибудь инструмент анализа трафика, такой как Wireshark и / или Networx , и попытайтесь выяснить, не мешает ли что-нибудь Win PC. Возможно, он проходит через прокси-сервер с настроенным ограничением скорости.

...