Ошибка запуска ускорения потока, если объект потока объявлен как член

Я написал класс с именем Task, который инкапсулирует boost::thread и позволяет переопределить метод run() для выполнения некоторой работы во вновь созданном потоке.

Вот базовый класс:

class Task {


    typedef boost::function<void ()> TaskEventCallback;
    typedef boost::unordered_map<string, TaskEventCallback> Callbacks;
    typedef boost::unordered_map<string, Callbacks> SessionTaskMap;
    typedef boost::unordered_map<TaskListener *, SessionTaskMap> ListenersMap;

    public:
        Task(NGSAppServer& server);
        Task(const Task& orig);
        virtual ~Task();

        virtual void run() = 0;
        bool start();
        bool pause();
        bool cancel();

        virtual bool registerListener(TaskListener *);
        virtual bool unregisterListener(TaskListener *);
        string getProgress();
        string getStatusMessage();

        boost::thread * getThread();

    protected:
        void postEvent(string event);
        void startThread();

        void setProgress(string progress);
        void setStatusMessage(string statusMessage);


        vector<TaskListener *> listeners;
        bool taskRunning;
        bool taskStarted;
        bool taskCanceled;
        bool taskEnded;
        NGSAppServer& server;

        boost::thread worker;
        boost::recursive_mutex mutex;

        ListenersMap listeners_map;

    private:
        string progress;
        string statusMessage;

};

Класс может публиковать события в нескольких сеансах http через класс сервера, но здесь это не уместно. Все работает правильно, поток запускается и успешно публикует события до конца работы. Это рабочий фрагмент:

        RestoreTask * task = new RestoreTask(application->getServer());
        TaskListener * listener = new TmpTL(*task, progressText, this);
        task->start();

И это класс Restore:

        class Restore : public Task {

        public:
            Restore(NGSAppServer& server);
            Restore(const Restore& orig);
            virtual ~Restore();

            virtual void run();

        private:
            ... stuffs ...
        };

Теперь я попытался разделить работу задачи восстановления на N подзадач (рабочий, также подкласс задачи). Вот новый метод восстановления Restore:

            std::vector<Worker *> workers;
            for(uint i = 0; i < 2; i++){
                //Start tread
                Worker _worker(this, server);
                _worker.start();
                workers.push_back(&_worker);
            }

            //Join Workers
            for(uint i = 0; i < 2; i++){
                workers.at(i)->getThread()->join();
            }

Этот код завершается ошибкой, так как при запуске дочернего потока создается ошибка при попытке запустить метод запуска класса Worker, поскольку он считается чисто виртуальным, и, кроме того, попытка заблокировать мьютекс в базовом классе Task завершается неудачно в этом утверждении:

void boost::recursive_mutex::lock(): Assertion `!pthread_mutex_lock(&m)' failed.

Непосредственное создание объекта Worker и запуск его (как для Restore) не создает никаких проблем!

На первый взгляд может показаться, что метод Restore run() завершился до дочерних потоков, удалив экземпляры Worker, а затем выполняя вызов run базового класса (чисто виртуального) и пытаясь получить доступ к уничтоженному мьютексу. (ПОЖАЛУЙСТА, ПРАВИЛИ МЕНЯ, ЕСЛИ Я НЕПРАВИЛЬНО ЗДЕСЬ!)

Использование отладчика для детализации проблемы, которую я обнаружил, это не так. Кажется, проблема остается в объявлении объектов Worker, поскольку следующие изменения делают код работающим без проблем:

            std::vector<Worker *> workers;
            for(uint i = 0; i < 2; i++){
                //Start tread
                Worker * _worker = new Worker(this, server);
                _worker->start();
                workers.push_back(_worker);
            }

            for(uint i = 0; i < 2; i++){
                workers.at(i)->getThread()->join();
                delete workers.at(i);
            }

Я бы предпочел создавать Workers без оператора new, поскольку мне не нужно сохранять эти объекты живыми после завершения Restore::run(), и я должен быть в состоянии гарантировать, что эти объекты будут существовать до завершения дочерних процессов из-за присоединение потоков (что было проверено с помощью отладчика).

Кто может найти узкое место здесь?

Я смог просто найти способ запустить мой код, но решение (но для меня важнее объяснение здесь) все еще отсутствует.

С уважением

1 ответ

Решение

_Worker выйдет из области видимости и будет уничтожен при повторении цикла for. Вы можете поставить отпечаток в деструкторе, чтобы проверить это.

То, что вы делаете во втором (новый..delete), является правильным, возможно, вы можете использовать smart_ptr / make_ptr, чтобы избежать удалений.

Вы также можете создать массив Workers вместо цикла for, в этом случае вам придется использовать ctor по умолчанию и передавать инициализаторы ( this, start) другим способом.

Другие вопросы по тегам