Утверждение о распределении задач TBB

#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;
}