2016-04-20 18 views
0

Ich habe eine n-Körper-Simulation, die ich parallelisiere. Ich verwende QT, also habe ich eine benutzerdefinierte QObject-Klasse mit dem Prozess, den ich parallelisiert habe. Es wird von einem anderen QObject in einem Thread gesteuert, der nur den Thread behandelt. Das Problem ist, dass jedes Mal, wenn ein Objekt mit einem anderen kollidiert, alle Threads gestoppt werden müssen, damit sie gelöscht und mit einem anderen Planetensystem neu erstellt werden können.Fehler beim Beenden eines Threads?

Hier ist die Thread-Handler-Klasse Quelle:

#include "threadhandler.h" 
#include <QEventLoop> 
#include "subprocess.h" 
#include <QThread> 
#include <iostream> 
#include <vector> 
#include <thread> 
#include <chrono> 

ThreadHandler::ThreadHandler(double scopeX, double scopeY) 
{ 
int size = 100; 
int idealNum = QThread::idealThreadCount(); 
int sizeForEach = size/idealNum; 

for(int i = 0 ; i< idealNum ;i++){ 
    SubProcess* tempSub = new SubProcess(i*sizeForEach, (i+1)*sizeForEach, scopeX, scopeY); 
    QThread* tempThread = new QThread; 
    tempSub->moveToThread(tempThread); 
    QEventLoop::connect(tempSub, SIGNAL(finished()), tempThread, SLOT(deleteLater())); 
    QEventLoop::connect(tempThread, SIGNAL(started()), tempSub, SLOT(process())); 
    QEventLoop::connect(tempThread, SIGNAL(finished()), tempSub, SLOT(deleteLater())); 
    QEventLoop::connect(tempSub, SIGNAL(collided()), this, 
         SLOT(refactorObjects())); 
    ThreadHandler::threads.push_back(tempThread); 
    ThreadHandler::objects.push_back(tempSub); 
} 
} 

void ThreadHandler::process(){ 
std::cout << ThreadHandler::threads.size() << std::endl; 
for(int i = 0; i < ThreadHandler::threads.size(); i++){ 
    std::cout << "starting " << i+1 << std::endl; 
    ThreadHandler::threads.at(i)->start(); 
} 
} 

void ThreadHandler::refactorObjects(){ 
SubProcess::condition = false; 
std::this_thread::sleep_for(std::chrono::seconds(1)); 
for(int i = 0 ; i < ThreadHandler::threads.size() ; i++){ 
    ThreadHandler::threads.at(i)->terminate(); 
} 
ThreadHandler::objects.clear(); 
SubProcess::sys.push_back(Body::collide(SubProcess::collidedAr.at(0), SubProcess::collidedAr.at(1))); 
SubProcess::sys.erase(std::remove(SubProcess::sys.begin(), SubProcess::sys.end(), 
            SubProcess::collidedAr.at(0)), SubProcess::sys.end()); 
SubProcess::sys.erase(std::remove(SubProcess::sys.begin(), SubProcess::sys.end(), 
            SubProcess::collidedAr.at(1)), SubProcess::sys.end()); 

int idealNum = QThread::idealThreadCount(); 
int sizeForEach = SubProcess::sys.size()/idealNum; 
for(int i = 0 ; i < idealNum ; i++){ 
    SubProcess* tempSub = new SubProcess(i*sizeForEach, (i+1)*sizeForEach); 
    tempSub->moveToThread(ThreadHandler::threads.at(i)); 
    ThreadHandler::objects.push_back(tempSub); 
    QEventLoop::connect(tempSub, SIGNAL(finished()), ThreadHandler::threads.at(i), SLOT(deleteLater())); 
    QEventLoop::connect(ThreadHandler::threads.at(i), SIGNAL(started()), tempSub, SLOT(process())); 
    QEventLoop::connect(ThreadHandler::threads.at(i), SIGNAL(finished()), tempSub, SLOT(deleteLater())); 
    QEventLoop::connect(tempSub, SIGNAL(collided()), this, 
         SLOT(refactorObjects())); 
} 

for(int i = 0; i < idealNum; i++){ 
    ThreadHandler::threads.at(i)->start(); 
} 
std::cout << "refactored" << std::endl; 
} 

Hier ist die Unterprozessquelle:

#include "subprocess.h" 
#include <iostream> 

std::vector<Body> SubProcess::sys; 
std::vector<Body> SubProcess::collidedAr; 
double SubProcess::timeScale; 
bool SubProcess::condition = true; 


SubProcess::SubProcess(double start, double end, double scopeX, double scopeY){ 
for(int i = start; i< end; i++){ 
    SubProcess::sys.push_back(Body::createRandomBody(scopeX, scopeY)); 
} 
this->start = start; 
this->end = end; 
} 
SubProcess::SubProcess(double start, double end){ 
this->start = start; 
this->end = end; 
} 

void SubProcess::process(){ 
while(SubProcess::condition){ 
    for(int i = start; i < end ; i++){ 
     for(int j = 0; j < SubProcess::SubProcess::sys.size() ; j++){ 
      if(!(SubProcess::sys.at(i)==SubProcess::sys.at(j))){ 
       double F = Body::getForce(SubProcess::sys.at(i), SubProcess::sys.at(j)); 
       if (F!=-1){ 
        double dX = SubProcess::sys.at(i).getX()-SubProcess::sys.at(j).getX(); 
        double dY = SubProcess::sys.at(i).getY()-SubProcess::sys.at(j).getY(); 
        SubProcess::sys.at(i).exertForce(-dY, -dX, F, timeScale); 
       } else { 
        SubProcess::collidedAr.clear(); 
        SubProcess::collidedAr.push_back(SubProcess::sys.at(i)); 
        SubProcess::collidedAr.push_back(SubProcess::sys.at(j)); 
        SubProcess::condition = false; 
        emit collided(); 
       } 
      } 
     } 
     SubProcess::sys.at(i).tick(timeScale); 
    } 
} 
emit finished(); 
} 

Jedes Mal, wenn ich es laufen, es funktioniert perfekt bis zu dem Punkt einer Kollision, wo ich Get:

Was macht mir nicht Sinn ist, dass die Threads am Anfang gelöscht werden g des Refactoring-Prozesses, und das schafft keinen Fehler. Aber aus irgendeinem Grund, nachdem die neuen Threads gestartet wurden, beendet etwas sie. Ich habe keine Ahnung, was hier überhaupt vor sich geht. Ich denke, dass ich die Fäden falsch entsorgere und neu schaffe und dass es eine bessere Methode dafür geben sollte.

Antwort

2

Im Allgemeinen führt das Beenden eines laufenden Threads zu einem nicht definierten Verhalten (im Gegensatz zu einem Thread, der in einem Zustand blockiert ist, in dem andere Daten konsistent sind). Sie sollten die Threads nicht beenden. Führen Sie einfach die deleteLater für die Teilprozesse aus, und sie werden sich automatisch entledigen. Ihre Threads werden nicht beeinflusst. Siehe this answer für warum es OK ist, deleteLater für Objekte in anderen Threads aufzurufen.

Sie sollten einen Pool von Threads zur Wiederverwendung behalten, ohne sie neu erstellen zu müssen. Sie können sogar die QThreadQueue dafür wiederverwenden.

Ein Standard QThread ist sehr unsicher zu zerstören, und Sie sollten stattdessen a version without that deficiency verwenden, wenn Sie können.

+0

Aber wenn ich deleteLater auf ihnen anrufe, wie kann ich sie dann wiederverwenden? Löscht das nicht sie? Oder ist ein vorübergehender Stopp? – Chris

+0

@ user1563074 Sie löschen die Worker-Objekte (die Subprozesse) natürlich nicht die Threads. –

+0

Wenn ich die Threads nicht zerstören/entsorgen muss, dann brauche ich eine benutzerdefinierte Thread-Klasse, die sicherer zu zerstören ist. Außerdem habe ich den obigen Code aktualisiert, und es führt immer noch die gleichen Fehler. – Chris

Verwandte Themen