QThread를 구현하는 올바른 방법은 무엇입니까… (예제를보십시오…)
QThread에 대한 Qt 문서에는 QThread에서 클래스를 만들고 실행 메서드를 구현하는 방법이 나와 있습니다.
아래는 4.7 Qthread 문서에서 발췌 한 것입니다 ...
자신의 스레드를 만들려면 QThread를 하위 클래스로 만들고 run ()을 다시 구현하십시오. 예를 들면 :
class MyThread : public QThread
{
public:
void run();
};
void MyThread::run()
{
QTcpSocket socket;
// connect QTcpSocket's signals somewhere meaningful
...
socket.connectToHost(hostName, portNumber);
exec();
}
그래서 내가 만든 모든 단일 스레드에서 그 작업을 수행했으며 대부분의 경우 잘 작동합니다 (내 개체에 moveToThread (this)를 구현하지 않고 훌륭하게 작동합니다).
나는 지난주에 걸림돌을 만났고 (내가 내 개체를 만든 곳에서 작업함으로써 그것을 극복 할 수 있었다) 다음 블로그 게시물을 발견 했습니다 . 여기에 기본적으로 QThread를 서브 클래 싱하는 것이이를 수행하는 올바른 방법이 아니라고 (그리고 문서가 올바르지 않다고 말합니다.)
이것은 Qt 개발자로부터 온 것이므로 언뜻보기에 관심이 있었고 더 생각해 보았을 때 그에게 동의했습니다. OO 원칙에 따라 클래스를 추가로 향상시키기 위해 클래스를 하위 클래스로 지정하고 싶을뿐입니다. 클래스 메서드를 직접 사용하는 것이 아니라 인스턴스화하는 것입니다.
사용자 정의 QObject 클래스를 스레드로 이동하고 싶다고 가정 해 보겠습니다. '올바른'방법은 무엇입니까? 그 블로그 게시물에서 그는 어딘가에 예가 있다고 '말합니다'...하지만 누군가가 나에게 그것을 더 설명해 줄 수 있다면 대단히 감사하겠습니다!
최신 정보:
이 질문이 많은 관심을 끌기 때문에 QThread를 구현하는 '적절한'방법으로 4.8 문서를 복사하여 붙여 넣었습니다.
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
나는 그들이 Worker::workerThread
불필요하고 그들의 예에서 결코 사용되지 않는 여분의 구성원을 포함한다는 것을 지적하는 것이 여전히 가치 있다고 생각합니다 . 그 조각을 제거하면 Qt에서 스레딩을 수행하는 방법에 대한 적절한 예입니다.
내가 추가 할 수있는 유일한 것은 QObject
s가 단일 스레드와 친화력이 있다는 추가 상태입니다 . 이것은 일반적으로 QObject
. 따라서 QObject
앱의 메인 스레드에서을 생성하고 다른 스레드에서 사용 moveToThread()
하려면를 사용 하여 선호도를 변경 해야합니다 .
이렇게하면 메서드 QThread
에서 개체 를 하위 클래스 로 만들고 생성 할 필요가 run()
없으므로 항목을 멋지게 캡슐화 할 수 있습니다.
해당 블로그 게시물에는 예제에 대한 링크가 포함되어 있습니다. 꽤 짧지 만 기본적인 아이디어를 보여줍니다. 귀하의 작성 QObject
, 당신의 신호를 연결의 당신을 만들 QThread
, 당신의 이동 QObjects
받는 사람 QThread
과 스레드를 시작합니다. 신호 / 슬롯 메커니즘은 스레드 경계가 적절하고 안전하게 교차되도록합니다.
해당 메커니즘 외부에서 개체에 대한 메서드를 호출해야하는 경우 동기화를 도입해야 할 수 있습니다.
Qt에는 아마도 익숙해 질 가치가있는 스레드 외에 다른 멋진 스레딩 기능 이 있지만 아직 그렇게하지 않았습니다. :)
다음은 QThread를 올바르게 사용하는 방법에 대한 한 가지 예 이지만 주석에 반영된 몇 가지 문제가 있습니다. 특히 슬롯이 실행되는 순서가 엄격하게 정의되어 있지 않기 때문에 다양한 문제가 발생할 수 있습니다. 2013 년 8 월 6 일에 게시 된 댓글은이 문제를 처리하는 방법에 대한 좋은 아이디어를 제공합니다. 나는 내 프로그램에서 이와 같은 것을 사용하고 있으며, 여기에 설명하는 몇 가지 예제 코드가 있습니다.
기본 아이디어는 동일합니다. 메인 스레드에있는 QThread 인스턴스, 내가 만든 새 스레드에있는 작업자 클래스 인스턴스를 만든 다음 모든 신호를 연결합니다.
void ChildProcesses::start()
{
QThread *childrenWatcherThread = new QThread();
ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
childrenWatcher->moveToThread(childrenWatcherThread);
// These three signals carry the "outcome" of the worker job.
connect(childrenWatcher, SIGNAL(exited(int, int)),
SLOT(onChildExited(int, int)));
connect(childrenWatcher, SIGNAL(signalled(int, int)),
SLOT(onChildSignalled(int, int)));
connect(childrenWatcher, SIGNAL(stateChanged(int)),
SLOT(onChildStateChanged(int)));
// Make the watcher watch when the thread starts:
connect(childrenWatcherThread, SIGNAL(started()),
childrenWatcher, SLOT(watch()));
// Make the watcher set its 'stop' flag when we're done.
// This is performed while the watch() method is still running,
// so we need to execute it concurrently from this thread,
// hence the Qt::DirectConnection. The stop() method is thread-safe
// (uses a mutex to set the flag).
connect(this, SIGNAL(stopped()),
childrenWatcher, SLOT(stop()), Qt::DirectConnection);
// Make the thread quit when the watcher self-destructs:
connect(childrenWatcher, SIGNAL(destroyed()),
childrenWatcherThread, SLOT(quit()));
// Make the thread self-destruct when it finishes,
// or rather, make the main thread delete it:
connect(childrenWatcherThread, SIGNAL(finished()),
childrenWatcherThread, SLOT(deleteLater()));
childrenWatcherThread->start();
}
배경 :
ChildProcesses 클래스는 spawn () 호출로 새 하위 프로세스를 시작하고 현재 실행중인 프로세스 목록을 유지하는 등의 하위 프로세스 관리자입니다. 그러나 자식 상태를 추적해야합니다. 즉, Linux에서는 waitpid () 호출을 사용하고 Windows에서는 WaitForMultipleObjects를 사용합니다. 예전에는 타이머를 사용하여 비 차단 모드로 호출했지만, 이제는보다 신속한 반응을 원하는데, 이는 차단 모드를 의미합니다. 그것이 스레드가 들어오는 곳입니다.
ChildrenWatcher 클래스는 다음과 같이 정의됩니다.
class ChildrenWatcher: public QObject {
Q_OBJECT
private:
QMutex mutex;
bool stopped;
bool isStopped();
public:
ChildrenWatcher();
public slots:
/// This is the method which runs in the thread.
void watch();
/// Sets the stop flag.
void stop();
signals:
/// A child process exited normally.
void exited(int ospid, int code);
/// A child process crashed (Unix only).
void signalled(int ospid, int signal);
/// Something happened to a child (Unix only).
void stateChanged(int ospid);
};
Here how it works. When all this stuff is started, the ChildProcess::start() method is called (see above). It creates a new QThread and a new ChildrenWatcher, which is then moved to the new thread. Then I connect three signals which inform my manager about the fate of its child processes (exited/signalled/god-knows-what-happened). Then starts the main fun.
I connect QThread::started() to the ChildrenWatcher::watch() method so it is started as soon as the thread is ready. Since the watcher lives in the new thread, that's where the watch() method is executed (queued connection is used to call the slot).
Then I connect the ChildProcesses::stopped() signal to the ChildrenWatcher::stop() slot using Qt::DirectConnection because I need to do it asynchronously. This is needed so my thread stops when the ChildProcesses manager is no longer needed. The stop() method looks like this:
void ChildrenWatcher::stop()
{
mutex.lock();
stopped = true;
mutex.unlock();
}
And then ChildrenWatcher::watch():
void ChildrenWatcher::watch()
{
while (!isStopped()) {
// Blocking waitpid() call here.
// Maybe emit one of the three informational signals here too.
}
// Self-destruct now!
deleteLater();
}
Oh, and the isStopped() method is just a convenient way to use a mutex in the while() condition:
bool ChildrenWatcher::isStopped()
{
bool stopped;
mutex.lock();
stopped = this->stopped;
mutex.unlock();
return stopped;
}
So what happens here is that I set the stopped flag when I need to finish, and then the next time isStopped() is called it returns false and the thread ends.
So what happens when the watch() loop ends? It calls deleteLater() so the object self-destructs as soon as control is returned to the thread event loop which happens just right after the deleteLater() call (when watch() returns). Going back to ChildProcesses::start(), you can see that there is a connection from the destroyed() signal of the watcher to the quit() slot of the thread. This means that the thread automatically finishes when the watcher is done. And when it's finished, it self-destructs too because its own finished() signal is connected to its deleteLater() slot.
This is pretty much the same idea as Maya posted, but because I use the self-destruct idiom, I don't need to depend on the sequence in which the slots are called. It's always self-destruct first, stop thread later, then it self-destructs too. I could define a finished() signal in the worker, and then connect it to its own deleteLater(), but that would only mean one connection more. Since I don't need a finished() signal for any other purpose, I chose to just call deleteLater() from the worker itself.
Maya also mentions that you shouldn't allocate new QObjects in the worker's constructor because they won't live in the thread you move the worker to. I'd say do it anyway because that's the way OOP works. Just make sure all those QObjects are children of the worker (that is, use the QObject(QObject*) constructor) - moveToThread() moves all the children along with the object being moved. If you really need to have QObjects that aren't children of your object, then override moveToThread() in your worker so it moves all the necessary stuff too.
Not to detract from @sergey-tachenov's excellent answer, but in Qt5 you can stop using SIGNAL and SLOT, simplify your code and have the advantage of compile time checking:
void ChildProcesses::start()
{
QThread *childrenWatcherThread = new QThread();
ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
childrenWatcher->moveToThread(childrenWatcherThread);
// These three signals carry the "outcome" of the worker job.
connect(childrenWatcher, ChildrenWatcher::exited,
ChildProcesses::onChildExited);
connect(childrenWatcher, ChildrenWatcher::signalled,
ChildProcesses::onChildSignalled);
connect(childrenWatcher, ChildrenWatcher::stateChanged,
ChildProcesses::onChildStateChanged);
// Make the watcher watch when the thread starts:
connect(childrenWatcherThread, QThread::started,
childrenWatcher, ChildrenWatcher::watch);
// Make the watcher set its 'stop' flag when we're done.
// This is performed while the watch() method is still running,
// so we need to execute it concurrently from this thread,
// hence the Qt::DirectConnection. The stop() method is thread-safe
// (uses a mutex to set the flag).
connect(this, ChildProcesses::stopped,
childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
// Make the thread quit when the watcher self-destructs:
connect(childrenWatcher, ChildrenWatcher::destroyed,
childrenWatcherThread, QThread::quit);
// Make the thread self-destruct when it finishes,
// or rather, make the main thread delete it:
connect(childrenWatcherThread, QThread::finished,
childrenWatcherThread, QThread::deleteLater);
childrenWatcherThread->start();
}
subclassing the qthread class will still run the code in the originating thread. I wanted to run a udp listener in application which is already using the GUI Thread(the main thread) and while my udp listener was working perfectly my GUI was frozen as it was blocked by the subclassed qthread event handlers. I think what g19fanatic posted is correct but you will also need the worker thread to succesfully migrate the object to the new thread. I found this post which describes in details of the Do's and Dont's of the threading in QT.
Must Read before you decide to subclass QThread !
'program story' 카테고리의 다른 글
파이썬 : self .__ class__ vs. type (self) (0) | 2020.12.02 |
---|---|
C # 용 CSS 파서가 있습니까? (0) | 2020.12.02 |
.NET 응용 프로그램의 비정상적인 잘못된 Viewstate 문제 (0) | 2020.12.02 |
자바에서 코 루틴 구현 (0) | 2020.12.02 |
JavaScript가 브라우저에 구현 된 유일한 클라이언트 측 스크립팅 언어 인 이유는 무엇입니까? (0) | 2020.12.02 |