Как изолировать вашу программу от вызовов к "плохому" API?
Когда я разработал часть (академического) программного обеспечения с использованием Java, я был вынужден использовать API, который был довольно плохо реализован. Это означает, что вызовы этого API для определенного набора входных данных иногда никогда не возвращаются. Это, должно быть, было ошибкой в программном обеспечении, поскольку предлагаемые алгоритмы были детерминированными, а иногда и заканчивались на наборе данных, иногда он запускался в бесконечный цикл на одном наборе данных...
Тем не менее, исправление API или переопределение его было просто вне сферы действия. У меня даже был источник, но API в значительной степени полагался на другие API, которые были недокументированы и без источника, и к тому времени исчезли из Интернета (или никогда там не были?). С другой стороны, этот "плохой" API был единственным, кто решил конкретную проблему, которую я имел, поэтому мне действительно пришлось придерживаться ее.
Вопрос: какой самый чистый способ иметь дело с API, который ведет себя так, ну, противно? Когда я столкнулся с этой проблемой, я решил поместить вызовы в API в отдельный поток. Затем другой поток иногда проверял, завершился ли этот поток. Если прошло определенное количество времени, я бы убил поток обработки с помощью Thread#stop()
и снова начал обработку, надеясь, что он вернется в следующий раз. Теперь я знаю (и знал тогда), что этот метод устарел и не должен использоваться. Но в этом академическом контексте было приемлемо, чтобы программное обеспечение потенциально запускалось в состояние undefined вместо того, чтобы сбой.
Было также неприемлемо просто игнорировать поток обработки, который запущен в бесконечный цикл, потому что он выполнял некоторые довольно интенсивные операции с процессором, которые значительно замедляли работу пользователя.
Другой способ, который я не пытался, - начать обработку в отдельном процессе, а не в потоке, потому что подпроцесс можно убить чисто, не помещая программное обеспечение в непоследовательное состояние. Или новый класс SwingWorker
(который еще не был доступен) выполнил эту работу? Он имеет метод cancel()
, но документы говорят, что он "пытается отменить выполнение этой задачи", поэтому он не похож на надежный подход.
Ответы
Ответ 1
Я бы рекомендовал использовать отдельный процесс. По сути, нет безопасного пути для одного потока, чтобы убить второй поток на Java, если только второй поток периодически проверяет, не прервана ли она.
Идеальное решение - использовать изоляты. Изоляция по существу является частной виртуальной машиной, с которой Java-приложение может создавать, управлять и обмениваться данными. В частности, родительское приложение может безопасно убить изоляцию и все ее потоки.
Ссылка: JSR-000121 Спецификация API изоляции приложений - окончательная версия
Проблема заключается в поиске JVM, который поддерживает Isolates.
Ответ 2
Я большой поклонник отдельных процессов для такого рода вещей.
Создайте подпроцесс и дождитесь результатов.
Если API не является детерминированным, поместите поток таймера в оболочку, который превращает плохой API в основную программу.
Таким образом, подпроцесс всегда заканчивается в течение данного времени. Он либо дает полезный результат, либо код выхода системы, который указывает на сбой.
Ответ 3
Как ответы @S.Lott, так и @Stephen C касаются того, как справиться с такой ситуацией, но я хотел бы добавить, что в неакадемической среде вы также должны искать замену API как можно скорее. В ситуациях, когда мы были заблокированы в плохом API, как правило, выбирая решение для решения по другим причинам, я работал над заменой своих функций со временем. Ваши клиенты не будут такими терпимыми, как ваш профессор, поскольку они фактически должны использовать ваше программное обеспечение (или нет!), А не просто оценивать его.
Есть, конечно, ситуации, когда использование клейкой ленты является адекватным выбором для решения проблемы. Когда это приводит к такому плохому поведению, как вы описали, однако, лучше не полагаться на него слишком долго и начать работать над реальным ремонтом.
Ответ 4
Лучшая задача - повторное внедрение рассматриваемого API. Однако, как вы говорите, это очень тяжелый вес и, вероятно, решение, выходящее за рамки.
следующая лучшая вещь должна была бы обернуть API, если это возможно. В принципе, если вы можете заранее определить, что именно происходит с наборами данных, которые вызывают отказ, вы можете отказаться от вызовов, чтобы гарантировать детерминизм. Не похоже, что это сработает и для вас, поскольку вы предполагаете, что повторение вызова с одним и тем же набором данных иногда прекращается, когда он имеет бесконечную петлю при предыдущем вызове.
Учитывая приведенные выше варианты, недоступны:
Я думаю, что ваше текущее решение потока - это лучший из плохих вариантов. Спиннинг процесса вызова метода кажется слишком тяжелым, чтобы быть приемлемым с точки зрения производительности, даже если это безопаснее, чем использование потоков. Thread.stop() очень опасен, но если вы религиозно запретите любую блокировку, вы можете с ней справиться.