tbb :: parallel_for_each не выполняется при отмене - PullRequest
1 голос
/ 21 января 2020

Я бегу tbb::parallel_for_each глубоко в пределах task_group. task_group отменяется, и это, кажется, заставляет tbb::parallel_for_each выходить без выполнения его постусловия. Вот минимальный тестовый пример:

tbb::task_group g;
std::vector<int> x { 0, 0, 0, 0 };
std::atomic<std::size_t> counter {0};
g.run([&x, &counter]() {
    std::cout << "in run()\n" << std::flush;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "in run(): slept\n" << std::flush;
    assert(tbb::task::self().is_cancelled());

    tbb::parallel_for_each(x, [&counter](int& y) {
        std::cout << "in run(): in parallel_for_each\n" << std::flush;
        ++y;
        ++counter;
    });
    assert(counter == x.size());
});
std::cout << "canceling\n" << std::flush;
g.cancel();
std::cout << "canceled " << g.is_canceling() << " " << tbb::task::self().is_cancelled() << std::endl;
assert(g.is_canceling());
std::cout << "canceled " << g.is_canceling() << " " << tbb::task::self().is_cancelled() << std::endl;
g.wait();
std::cout << "canceled " << g.is_canceling() << " " << tbb::task::self().is_cancelled() << std::endl;

То есть: он имеет tbb::task_group вызов run для функции, которая ждет 0,1 с, а затем зацикливается на векторе. Он подсчитывает, сколько итераций делает l oop. Затем cancel() s группа задач. Вывод:

canceling
in run()
canceled 1 0
canceled 1 0
in run(): slept
Assertion failed: (counter == x.size()), function operator(), file test.cpp

То есть внутренний l oop никогда не происходит. Однако мое удивление заключается в том, что tbb::parallel_for_each вызывается и выходит без исключения, но его пост-условие (выполнение l oop) не выполняется!

  1. Это ожидаемое поведение? Документы не дают никаких предостережений: https://software.intel.com/en-us/node/506160
  2. Как мне проверить это поведение? Всегда ли мне нужно проверять tbb::task::self().is_cancelled() после вызова tbb::parallel_for_each, хочу ли я быть уверенным, что он действительно справился со своей задачей?
  3. Не должен ли tbb::parallel_for_each вызвать исключение в этом случае (или хотя бы вернуть bool)?

Я могу «исправить» это, используя изолированный контекст (https://software.intel.com/en-us/node/506075), например:

    tbb::task_group_context root(tbb::task_group_context::isolated);
    tbb::parallel_for_each(x, [&counter](int& y) {
        std::cout << "in run(): in parallel_for_each\n" << std::flush;
        ++y;
        ++counter;
    }, root);

, но на В данный момент я не уверен, когда смогу доверить tbb::parallel_for_each выполнению своей работы.

1 Ответ

1 голос
/ 21 января 2020

Внутри группы задач вызовы tbb::parallel_for_each подчиняются контексту группы задач.

Отмена этой группы задач отменяет ее и все подчиненные группы задач. Я не могу найти, где в документации определена «группа подчиненных задач», но, похоже, это группы в группе в «дереве задач».

Отмененный tbb::parallel_for_each не гарантирует, что все его итерации завершены. Условие публикации не было нарушено.

Вы отменили группу задач, которая отменяет неявную группу задач подчиненного tbb::parallel_for_each, поэтому она не выполняет операции.

Ваш изолированный контекст предотвращает отмена распространения в параллель для каждой группы задач. Вы можете повторить эффект в этом коде, отменив root перед вызовом parallel_for_each.

...