2013-01-06 10 views
5

Sto provando ad uccidere/cancellare un QThread. Ho seguito l'attuazione di https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/Qt - come uccidere QThread

creo il QThread in questo modo:

workerThread = new QThread(); 
ImageProcessor* worker = new ImageProcessor(); 
worker->moveToThread(workerThread); 
connect(workerThread, SIGNAL(started()), worker, SLOT(process())); 
connect(worker, SIGNAL(finished()), workerThread, SLOT(quit())); 
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); 
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); 
workerThread->start(); 

e cercare di fermarlo su un determinato evento UI chiamando:

workerThread->quit(); 

mio lavoratore si presenta così:

class ImageProcessor : public QObject 
{ 
Q_OBJECT 
public: 
explicit ImageProcessor(QObject *parent = 0); 
~ImageProcessor(); 

signals: 
void finished(); 

public slots: 
void process(); 

private: 
//... 
}; 

Dove processo chiama un'API con calcoli pesanti

void ImageProcessor::process() 
{ 
// API call... 

emit finished(); 
} 

Il mio problema è, che dopo aver chiamato uscire sul filo, il filo continua a funzionare cioè i calcoli delle API non si fermano. Poiché il lavoratore chiama solo l'API, non posso lavorare con un controllo di annullamento nel ciclo di lavoro. Qualche idea?

Grazie in anticipo!

+0

un'occhiata a questa risposta - http://stackoverflow.com/questions/14092915/qt-qthread-exclusively-for-object/14178944#14178944, può essere utile per voi –

risposta

1

ovvero i calcoli dell'API non si fermano.

Quindi si tenta di fermarlo durante un calcolo?

QThread :: smette di interromperlo solo dopo è stato calcolato tutto.

È possibile dividere il calcolo in attività più piccole o persino aggiungere un flag di annullamento. Avere un metodo normale sull'oggetto ImageProcessor che lo imposta:

class ImageProcessor{ 
    void stop(){ 
    canceled = true; 
    } 
private: 
    bool canceled; 
} 

e quindi verificare se (annullato) ritorno; nello slot di processo. (un bool di sola scrittura è un tipo di thread sicuro)

Quindi chiamare stop(); sull'oggetto e quit() sulla discussione.

+0

Il problema qui è, che io chiamo solo una funzione API che fa calcoli pesanti. Non riesco a romperlo in pezzi più piccoli e quindi non posso controllare per annullato. L'API kindof inizia la sua vita e non so fermarlo ... –

+1

Perché non lasciarlo correre? Se chiami 'workerThread-> setPriority (QThread :: IdlePriority)' anche non userà molte risorse ... – BeniBela

+0

Puoi eseguire il thread fino alla fine e se 'cancel' è vero allora non emettere il segnale finito. – MichK

1

Non è possibile interrompere un QThread che è bloccato, almeno non in modo sicuro.

Ad esempio, se hai inserito questa funzione

void neverStop() { 
    int *arr = new int[1024*1024]; 
    for (;;) { 
    } 
} 

il filo non risponderà a qualsiasi messaggio smettere, e mentre la maggior parte sistemi operativi hanno un modo per forzare la terminazione di un thread, la memoria allocata non sarà correttamente rilasciato.

0

Invece di utilizzare thread->quit() è necessario includere un flag di annullamento all'interno del ciclo di elaborazione (in base al concetto di semahpore). In questo modo sei in grado di fermare correttamente il lavoratore dall'interno, ad es. arrestando l'elaborazione e emettendo il segnale finito().

È possibile impostare il flag implementando uno slot per l'annullamento (ad esempio slot_cancelWorker()) all'interno dell'operatore. Poi si può collegare con la vostra interfaccia grafica:

connect(myPushButton, SIGNAL(clicked()), worker, SLOT(slot_cancelWorker())); 

Naturalmente, all'interno di funzione che si controlla per gli eventi in attesa che si elaborazione. Pertanto, è necessario chiamare periodicamente lo QCoreApplication::processEvents().

2

Il tuo compito pesante è così semplice che vorrei usare QtConcurrent::run e dimenticare QThread.

ImageProcessor* worker = new ImageProcessor(); 
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); 
QtConcurrent::run(worker, &ImageProcessor::process); 

Questo fa esattamente quello che ti serve e sarà anche un po 'più veloce, poiché utilizzerà filo dal pool di thread, creando invece un nuovo thread.

Ricordarsi di utilizzare Qt::AutoConnection (valore predefinito).

Per interrompere l'elaborazione con grazia quello che dovete fare una polarità di una variabile nel metodo processo:

bool ImageProcessor::needEndNow() { 
    QMutexLocker locker(mutex); 
    return endNowRequested; 
} 

void ImageProcessor::requestEndNow() { 
    QMutexLocker locker(mutex); 
    endNowRequested = true; 
} 

void ImageProcessor::process() 
{ 
    while(!needEndNow()) { 
     // nextStep of processing 
    } 

    emit finished(); 
} 
1

Se si dispone di qualcosa come questo, senza alcun ciclo:

void ImageProcessor::process() 
{ 
    worker_api(); // that takes a lot of time 
    emit finished(); 
} 

C'è poco puoi farlo con QThread. È possibile, dal migliore al peggiore:

  • Forse l'API che si sta utilizzando ha un'altra chiamata per interrompere quella chiamata di lavoro. Ma dal momento che stai chiedendo qui, immagino che non ci sia nulla di simile.
  • Avvia la chiamata di lavoro da un processo diverso utilizzando QProcess e kill quando non è più necessario. Pro: nessuna preoccupazione per il clean-up. Contras: il recupero dei risultati potrebbe essere difficile.
  • QThread::terminate, ma è sconsigliato (vedere warnings in the documentation: in pratica, nessuna pulizia).
  • In Windows, QThread::currentThreadId fornisce un numero che è possibile utilizzare con altre chiamate del sistema operativo (vedere warnings in the documentation).