Ошибка запуска ускорения потока, если объект потока объявлен как член
Я написал класс с именем 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) другим способом.