Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

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

EDIT: соответствующий код

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.

4b9b3361

Ответ 1

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

Мне удалось показать, что программа может проявлять такое же медленное поведение на моей машине Linux и действительно может запустить полный дроссель на проблемной машине Win-7. Суть проблемы заключается в некотором повреждении файлов системы/кеша, которые используются для хранения результатов предыдущих запросов, и в целом ускорить анализ. Вы должны любить иронию, в этом случае они, по-видимому, были причиной ЭКСТРЕМАЛЬНОГО медленного анализа. Оглядываясь назад, я должен был знать (бритва a la Occam)...

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Потребительский поток:

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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