SubProgressMonitor и SubMonitor — это классы Eclipse, используемые для индикации хода выполнения задания или задачи в рабочей среде Eclipse. SubProgressMonitor устарел в полупоследнем выпуске Eclipse, и предпочтительным (и, по-видимому, лучшим) классом монитора прогресса является SubMonitor. Если вы создаете новые классы, вам обязательно следует использовать SubMonitor: дополнительная информация доступна в этой статье Eclipse (https://eclipse.org/articles/Article-Progress-Monitors/article.html) .

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

Основной шаблон SubProgressMonitor: try / beginTask / new / work / done:

Основной шаблон SubMonitor — преобразовать / разделить / (сработало):

Работая с кодом, который взаимозаменяемо использовал как SubProgressMonitor, так и SubMonitor, я обнаружил, что работа, о которой сообщается (разделяется/обрабатывается) из дочерних SubMonitors, не была сообщается родительскому SubProgressMonitors. Поиск в Интернете очевидных решений не дал никаких предложений, поэтому пришло время загрузить код в оба класса и посмотреть, что происходит.

И SubMonitor, и SubProgressMonitor реализованы в репозитории rt.equinox.bundles в подключаемом модуле org.eclipse.equinox.common.

Основной репозиторий:
https://git.eclipse.org/c/equinox/rt.equinox.bundles.git/

Зеркало Github:
https://github.com/eclipse/rt.equinox.bundles

Чтобы получить репозиторий, используйте:
git clone git://git.eclipse.org/gitroot/equinox/rt.equinox.bundles.git

SubMonitor реализован в:
rt.equinox.bundles\bundles\org.eclipse.equinox.common\src\org\eclipse\core\runtime\SubMonitor.java

SubProgressMonitor реализован в:
rt.equinox.bundles\bundles\org.eclipse.equinox.common\src\org\eclipse\core\runtime\SubProgressMonitor.java

Импортируйте подключаемый модуль org.eclipse.equinox.common в свою рабочую область (Импорт › Git › Projects from Git).

(Если вы нацелены на выпуск до Neon Eclipse, используйте newChild(…) вместо split(..). почти то же самое, за исключением того, что split(…) также выполняет проверки отмены». )

Конкретная проблема, с которой я столкнулся, заключалась в том, что пользователю в пользовательском интерфейсе рабочей среды Eclipse не сообщалось о ходе выполнения (рабочие тики), хотя методы передавались IProgressMonitor и правильно вызывали SubMonitor.(convert/newChild).

Это может быть вызвано несколькими причинами:
1) NullProgressMonitor был заменен IProgressMonitor с поддержкой задания
2) Шкала делений слишком большой (например, вы указали, что ваша работа будет включать 1000 тиков, но вы указали только работу в 1–10 тиков, что может округляться до 0%)
3) Один из родительских мониторов отбрасывает причитающуюся работу к предыдущему нарушению «контракта» SubProgressMonitor.

Каждый SubMonitor/SubProgressMonitor имеет одного родителя. Эти мониторы образуют одноветвистое иерархическое дерево мониторов, начиная с монитора хода выполнения, предоставляемого заданием Eclipse. SubProgressMonitor и SubMonitor могут использоваться вместе, поэтому дерево мониторов может выглядеть следующим образом:

IProgressMonitor provided by job (actually an inner class of Job implementing the interface)
\_ SubProgressMonitor
 \_ SubProgressMonitor
  \_ SubMonitor
   \_ etc

Каждый SubMonitor/SubProgressMonitor имеет не более одного родителя. При отладке к родителю монитора можно получить доступ из переменной экземпляра класса монитора:

  • SubMonitor.root.root типа IProgressMonitor
  • SubProgressMonitor.progressMonitorтипа IProgressMonitor

Имея эту информацию, вы можете изучить дерево проблемного монитора, начав с дочернего монитора и «карабкаясь по дереву» до тех пор, пока не будет выявлена ​​проблема.

Как отлаживать родителя NullProgressMonitor в дереве монитора

Найдите место в вашем коде, где работа выполняется, но о ней не сообщается. Это может быть вызов IProgressMonitor.worked(…)/SubMonitor.newChild(…) или SubMonitor.split(…). Добавьте точку останова в этот метод и отладьте приложение, чтобы вызвать запуск оператора.

Шаг внутри вызова работал/разделил вызов. Отсюда вы можете проверить движение вверх по дереву мониторов выполнения, чтобы определить, является ли какой-либо из родителей NullProgressMonitor. Если тип — SubMonitor, проверьте переменную root.root; если тип — SubProgressMonitor, проверьте переменную progressMonitor.

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

Как отладить родительский монитор, отбрасывая дочернюю работу

Это сложнее, чем простое сканирование дерева на наличие NullProgressMonitor, как указано выше. Класс SubProgressMonitor, как написано, будет молча отбрасывать отчет о работе, если пользователь нарушил «контракт» класса.

Контракт класса, относящийся к этой задаче, таков:

  • Вы должны сразу вызвать beginTask(…) на мониторе; если вы вызовете его ноль раз (например, никогда не вызывайте beginTask(…) ), работа будет проигнорирована.
  • Вы не должны вызывать beginTask(…) дважды на одном и том же мониторе; если вы это сделаете, вся последующая работа будет проигнорирована.

Это можно увидеть в методе SubProgressMonitor.internalWorked(…), где, если «nestedBeginTasks» не равно единице, вызов подавляется без предупреждения.

Хотя мы ничего не можем сделать с реализацией SubProgressMonitor (в любом случае вы должны использовать SubMonitor), мы можем что-то сделать с частью «без предупреждения».

Давайте оснастим класс, добавив собственные предупреждения при нарушении контракта:

1) Добавьте следующие поля в SubProgressMonitor:

2) Замените конструктор с 3 аргументами на:

3) Замените beginTask(…) следующим образом:

4) Замените internalWorked(…) следующим образом:

Обратите внимание на пункт TODO внутри if. Поскольку этот блок кода будет часто запускаться другим кодом в рабочей среде, необходимо отфильтровать все потоки, КРОМЕ вашего собственного кода.

С приведенными выше изменениями теперь вы должны получать предупреждения (вместе с трассировкой стека) для следующих двух сценариев:

  1. Несколько вызовов beginTask(…); для вызова после первого будет сообщена трассировка стека вместе с действительным исходным первым вызовом.
  2. Нулевые вызовы beginTask(…); при вызове internalWorked(…) будет сообщена трассировка стека, а также трассировка стека конструкции объекта.

Инструментарий SubMonitor в большинстве случаев не требуется. Он умнее в том, как он написан, и включает предупреждения, когда его контракт нарушается. Подробности смотрите в методе logProblem(…) в SubMonitor.