Отмена произвольных заданий, запущенных в thread_pool - PullRequest
0 голосов
/ 23 декабря 2019

Есть ли способ для пула потоков отменить выполняемую задачу? Еще лучше, есть ли безопасная альтернатива для отмены непрозрачных вызовов функций по запросу в thread_pools?

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

Дополнительно

Бонус, если отмена немедленная, но допустима, если у отмены есть какое-то временное ограничение «гарантии» (например, отмена в течение 0,1 секунды выполнения рассматриваемого потока)

Подробнее

Я не ограничен использованием Boost.Thread.thread_pool или какой-либо конкретной библиотеки. Единственным ограничением является совместимость с C ++ 14 и способность работать как минимум на ОС на базе BSD и Linux.

Задачи обычно связаны с обработкой данных, предварительно компилируются и загружаются динамически с использованием C-API (extern "C") и, следовательно, являются непрозрачными объектами. Цель состоит в том, чтобы выполнять сложные вычислительные задачи с возможностью отмены их, когда пользователь отправляет прерывания.

Во время запуска, thread_id для конкретной задачи известен, и, таким образом, к некоторому API можно обратиться за дополнительной информацией, еслитребуется.

Отказ от ответственности

Я знаю, что использование ручных ручных нитей для отмены / выхода нитей не рекомендуется и является признаком плохого дизайна. Я также не могу изменить функции, используя boost::this_thread::interrupt_point, но могу обернуть их в лямбды / другие конструкции, если это поможет. Я чувствую, что это сложная ситуация, поэтому альтернативные предложения приветствуются, но они должны быть минимально навязчивыми в существующей функциональности и могут быть драматичными по своему объему для обсуждаемого набора функций.

РЕДАКТИРОВАТЬ:

Уточнение

Я думаю, это должно было быть в разделе «Подробнее», но я хочу, чтобы он оставался отдельным, чтобы показать, что существующие 2 ответа основаны на ограниченной информации. Прочитав ответы, я вернулся к чертежной доске и придумал следующие «ограничения», поскольку поставленный вопрос был слишком общим. Если я хочу опубликовать новый вопрос, пожалуйста, дайте мне знать.

Мой интерфейс обещает ввод "const" (не изменяемый ввод в стиле функционального программирования), используя при необходимости mutexes / copy-by-value и проходя мимоconst& (и ожидал, что поток будет вести себя хорошо).

Я также неправильно использовал термин «произвольный», поскольку задания не являются произвольными (эмпирически говоря) и имеют следующие ограничения:

  • некоторые, которые загружают из "интернета", уже используют "условную переменную"
  • , не нарушающие правильность const
  • могут порождать другие потоки, но они не должны переживать родительский
  • может использовать мьютекс, но они не могут существовать вне тела функции
  • вывод через atomic<shared_ptr>, передаваемый в качестве аргумента
  • чистые функции (без общего состояния с внешним) **
  • ** может быть лямбда-связыванием функтора, и в этом случае функция должна убедиться, что ее структуры данных не повреждены (как обычно, состояние 1 или 2 atomic<inbuilt-type>). Обычно внутреннее состояние запрашивается из внешней базы данных (подобная архитектура, как cookie + веб-сервер, и вкладка / браузер может быть закрыта в любое время)

Эти ограничения не записываются как контракт илиничего, а точнее я обобщил на основе «модулей», используемых в настоящее время. Задания являются произвольными с точки зрения того, что они могут сделать: GPU / CPU / Internet - все это честно.

Невозможно вставить периодическую проверку из-за интенсивного использования библиотеки. Библиотеки (не принадлежащие нам) не были предназначены для периодической проверки переменной условия, поскольку это повлекло бы за собой снижение производительности в общем случае, и перезапись библиотек невозможна.

Ответы [ 2 ]

0 голосов
/ 24 декабря 2019

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

  • theРесурсы, принадлежащие компонентам, должны управляться извне (система знает, какой компонент использует какие ресурсы)
  • все обращения должны быть косвенными
  • изменения общих ресурсов должны быть безопасными и обратимыми до завершения

Это позволило бы системе очистить ресурс, остановить операции, отменить незавершенные изменения ...

Ни одно из этих свойств не дешево; все свойства потоков полностью противоположны этим свойствам .

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

Потоки обращаются к общим объектам напрямую. Поток может запускать модификации общих объектов;после отмены такие модификации, которые были бы частичными, неэффективными, некогерентными, если были остановлены в середине операции.

Отмененные потоки могли оставить заблокированные мьютексы вокруг. По крайней мере последующие обращения к этим мьютексам другими потоками при попытке доступа к общему объекту приведет к взаимоблокировке.

Или они могут найти некоторую структуру данныхв плохом состоянии.

Обеспечение безопасного отмены для произвольных не взаимодействующих потоков невозможно даже при очень крупномасштабных изменениях для объектов синхронизации потоков. Даже не путем полной реорганизации потоковых примитивов.

Вы должны были бы создавать потоки почти как полные процессы, чтобы иметь возможность это делать;но тогда это не будет называться потоком!

0 голосов
/ 24 декабря 2019

Есть ли способ для пула потоков отменить выполняемую задачу?

Не на этом уровне универсальности, нет, и также нет, если задача, выполняющаяся в потоке, являетсяреализовано в произвольном порядке в C или C ++. Вы не можете завершить выполняющуюся задачу до ее завершения, не завершив ее весь поток, кроме как с помощью совместной задачи.

Еще лучше, есть ли безопасная альтернатива для отмены вызовов непрозрачных функций по требованию вthread_pools?

Нет. Единственный способ получить (приблизительно) вытеснение по требованию определенного потока - это доставить ему сигнал (то есть не блокировать или игнорировать) через pthread_kill(). Если такой сигнал завершает поток, но не весь процесс, он автоматически не обеспечивает освобождение выделенных объектов или управление состоянием мьютексов или других объектов синхронизации. Если сигнал не завершает поток, то прерывание может привести к неожиданным и нежелательным эффектам в коде, не предназначенном для такого использования сигнала.

Убивать весь процесс - плохая идеяи использование собственного дескриптора для выполнения pthread_cancel или аналогичного API-интерфейса является только последним средством.

Обратите внимание, что pthread_cancel() может быть заблокирован потоком, и что даже если он не заблокирован, его эффекты могут быть отложены на неопределенное время,Когда эффекты действительно происходят, они не обязательно включают очистку памяти или объекта синхронизации. Для достижения этой цели поток должен сотрудничать с собственной отменой.

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

...