Спорадические проблемы при запуске многопоточного 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
.
Ответы
Ответ 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 ядра
- Мой следующий шаг - добавить какое-то время для регистрации/анализа времени выполнения, чтобы увидеть, что происходит более подробно.
- а также немного переписать потоки отправки/получения, чтобы узнать, помогает ли она
Когда я узнаю что-то новое/полезное добавит его.