0

我编写了一个名为 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();

这是恢复类:

        class Restore : public Task {

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

            virtual void run();

        private:
            ... stuffs ...
        };

现在我尝试将还原任务的工作拆分为 N 个子任务(Worker,也是任务的子类)。这里是恢复的新运行方法:

            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 类 run 方法时创建了一个 sigfault,因为它被报告为纯虚拟,而且尝试在 Task 基类上锁定互斥锁在此断言上失败:

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

直接创建一个 Worker 对象并启动它(对于恢复)不会产生任何问题!

乍一看,Restore run() 方法似乎在子线程之前结束,删除了 Worker 实例,然后在运行时调用基类(纯虚拟)并尝试访问已破坏的互斥体。(如果我在这里错了,请纠正我!)

使用调试器深入研究我发现的问题并非如此。问题似乎停留在 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);
            }

我更喜欢在没有 new 运算符的情况下创建 Workers,因为在 Restore::run() 完成后我真的不需要让这些对象保持活动状态,并且我应该能够保证这些对象仍然存在,直到孩子们完成,因为线程加入(已通过调试器验证)。

谁能找到这里的瓶颈?

我已经能够找到一种方法来运行我的代码,但仍然缺少解决方案(但对我来说更重要的是这里的解释)。

最好的祝福

4

1 回答 1

0

当 for 循环重复时,_worker 将超出范围并被破坏。您可以在析构函数中打印以验证这一点。

您在第二个(新的 ..delete)中所做的事情是正确的,也许您可​​以使用 smart_ptr / make_ptr 来避免删除。

您还可以创建一个 Workers 数组而不是 for 循环,在这种情况下,您必须使用默认 ctor,并以其他方式传递初始化程序( this、start )

于 2015-11-29T15:33:25.420 回答