#c #tbb
#c #tbb
Вопрос:
Я пытаюсь пройти по дереву с помощью задач и продолжений TBB. Код приведен ниже. Когда я запускаю код, он продолжает прерываться (часто, хотя и не всегда) со следующей ошибкой:
Утверждение t_next->state()==ошибка task::allocated в строке 334 файла ../../src/tbb/custom_scheduler.h Подробное описание: если task::execute() возвращает задачу, она должна быть помечена как выделенная
Что может быть причиной этой проблемы?
template<class NodeVisitor>
void
traverse_tree(NodeVisitoramp; nv)
{
TreeTraversal<NodeVisitor>amp; tt = *(new(task::allocate_root()) TreeTraversal<NodeVisitor>(nv));
task::spawn_root_and_wait(tt);
}
template<class NodeVisitor>
class TreeTraversal: public task
{
public:
struct Continuation;
public:
TreeTraversal(NodeVisitor nv_):
nv(nv_) {}
task* execute()
{
nv.pre();
Continuation* c = new(allocate_continuation()) Continuation(nv);
c->set_ref_count(nv.size());
for (size_t i = 0; i < nv.size(); i)
{
TreeTraversalamp; tt = *(new(c->allocate_child()) TreeTraversal(nv.child(i)));
spawn(tt);
}
if (!nv.size())
return c;
return NULL;
}
private:
NodeVisitor nv;
};
template<class NodeVisitor>
class TreeTraversal<NodeVisitor>::Continuation: public task
{
public:
Continuation(NodeVisitoramp; nv_):
nv(nv_) {}
task* execute() { nv.post(); return NULL; }
private:
NodeVisitor nv;
};
Комментарии:
1. Вы используете вместо new, но вы никогда не уничтожаете, поэтому деструкторы не вызываются. Я понятия не имею, является ли это проблемой.
2. Я думаю, что TBB должен позаботиться об этом уничтожении. Однако я могу ошибаться.
3. Я ничего не знаю о TBB, кроме просмотра вашего кода. Это возможно.
4. @MooingDuck: Да, предполагается, что TBB позаботится об уничтожении задач после выполнения.
5. @AlexeyKukanov: Они тоже уничтожают себя? Я никогда ни о чем подобном не слышал. Это странно.
Ответ №1:
Я никогда раньше не видел, чтобы задача выделялась как продолжение, а затем возвращалась из execute()
. Это может быть причиной сбоя утверждения (обновление: эксперимент показал, что это не так, подробности см. Ниже).
Между тем, вы можете изменить код TreeTraversal::execute()
, чтобы он был примерно таким:
nv.pre();
if (!nv.size())
nv.post();
else {
// Do all the task manipulations
}
return NULL;
Обновление: упрощенный тест, показанный ниже, хорошо работал на моем двухъядерном ноутбуке. Это заставляет меня предположить возможное повреждение памяти в вашем реальном коде, и в этом случае предложенная выше перетасовка может просто скрыть проблему, но не исправить ее.
#include "tbb/task.h"
using namespace tbb;
class T: public task {
public:
class Continuation: public task {
public:
Continuation() {}
task* execute() { return NULL; }
};
private:
size_t nv;
public:
T(size_t n): nv(n) {}
task* execute() {
Continuation* c = new(allocate_continuation()) Continuation();
c->set_ref_count(nv);
for (size_t i = 0; i < nv; i) {
Tamp; tt = *(new(c->allocate_child()) T(nv-i-1));
spawn(tt);
}
return (nv==0)? c : NULL;
}
};
int main() {
Tamp; t = *new( task::allocate_root() ) T(24);
task::spawn_root_and_wait(t);
return 0;
}