我遇到的问题是,我QThreads根据大量文章决定实施他们应该采用的方式:
http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
并且问题在于,因为算法是在单独的QObject(包装QThread)中运行的.我该怎么称呼Thread::Sleep或者说什么呢?任何想法?
该软件的一个小描述.基本上我的申请解决了TSP(旅行商问题).随着搜索的进行,它将历史中的所有状态保存为frames...(如可视框架).搜索算法将在一个线程上运行.主线程正在使用GUI进行处理.然后有Mediaplayer类似的线程告诉Main线程在屏幕上显示什么帧.那么睡眠在哪里?在gui中有一个滑块,用户可以使用它来快进或以正常速度前进.滑块通过信号槽告诉Mediaplayer线程更快或更慢.
我是StackOverflow的新手,想知道我是否正确行事:
我正在编写一个简单的Qt应用程序来测试多线程(我也是一个全新的东西).我创建了一个包含小部件的MainWindow,以及一个继承QThread并覆盖run()方法的类MyThread.
应用程序只显示两个按钮,"启动计数器"和"停止计数器",以及一个文本字段.当按下"启动计数器"时,创建工作线程并在后台运行,在while循环中连续递增计数器并用更新的值发信号通知主线程(GUI所在的位置).按下"停止计数器"时,会向主线程发送一个停止while循环的信号,计数器停止,直到再次按下"启动计数器".
这非常好用......但这是最好的方法吗?我是新手,并阅读了很多人说"不要继承QThread"和其他人说"子类QThread",这有点令人困惑.如果这不是实现这种事情的最佳方式(在后台线程中使用"start"和"stop"按钮运行计算密集型循环),那是什么?如果我做错了,我该怎么做呢?我不想学习错误的方法.
谢谢!这是代码:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public slots:
void stopRunning();
protected:
virtual void run();
signals:
void signalValueUpdated(QString);
private:
bool isRunning;
};
Run Code Online (Sandbox Code Playgroud)
MyThread.cpp
#include "MyThread.h"
#include <QString>
void MyThread::run()
{
qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
static int value=0; //If this is not static, then it is reset to 0 every time this function is called.
isRunning = 1;
while(isRunning == 1)
{
QString string = QString("value: …Run Code Online (Sandbox Code Playgroud) 我使用两个QGLWidgets.一个用于加载纹理,一个用于渲染,但它不起作用.
我在http://blog.qt.digia.com/blog/2011/06/03/threaded-opengl-in-4-8/上使用了以下说明
纹理上传线程上传许多(或大)纹理通常是一项昂贵的操作,因为数据量被推送到GPU.同样,这是可能不必要地阻止主线程的那些操作之一.在4.8中,您可以通过创建一对共享QGLWidgets来解决此问题.其中一个小部件在一个单独的线程中是最新的,但从未在屏幕上显示.主线程通知上传线程要上传哪些图像,上传线程只是在每个图像上调用bindTexture(),然后在每个图像完成时通知主线程,以便可以将其绘制到屏幕上.
使用Qt 4.8和MinGW它工作正常,但现在我使用Qt 5.1和MSVC.当我想让线程中的小部件变为当前时,我收到错误:
无法使QOpenGLContext在另一个线程中保持最新状态
我理解错误,但我该如何解决它.当我没有设置小部件当前我无法加载纹理(在bindTexture()函数冻结).我也想知道,为什么它适用于我的旧QT版本.当出现错误时,我可以按"忽略错误",程序无论如何都会加载纹理.
以下是一些示例代码:
加载纹理:
GLContext::GLContext(QWidget *parent, QGLWidget *myDisplayWidget) :
QGLWidget(parent,myDisplayWidget)
{
}
Run Code Online (Sandbox Code Playgroud)
...
GLContext* myTextureWidget = new GLContext(this,myDisplayWidget);
Run Code Online (Sandbox Code Playgroud)
...
void TextureLoadingThread::run()
{
makeCurrent(); //Here is the bug!
QImage *im = new QImage(filename);
GLuint textid = myTextureWidget->bindTexture(*im, GL_TEXTURE_2D, GL_RGBA);
}
Run Code Online (Sandbox Code Playgroud)
编辑:
当我将myTextureWidget的上下文移动到它运行的线程时,但是当GUI将构建时,我从API获得makeCurrent Error(堆栈跟踪在QT5Widgetsd中的QLineEdit :: setPlaceHolderText函数中说明).当我在主窗口显示几秒后将myTextureWidget移动到线程时,一切正常.但是我怎么知道什么时候qt完成了所有GUI构建的东西?我使用QGLWidget视口将GUI绘制到QGraphicsView.
myTextureWidget->context()->moveToThread(myTextureLoadingThread);
Run Code Online (Sandbox Code Playgroud) QThread当我按下按钮Run时,我想启动我.但编译器输出以下错误:
QThread: Destroyed while thread is still running
ASSERT failure in QThread::setTerminationEnabled(): "Current thread was not started with QThread.", file thread\qthread_win.cp.
Run Code Online (Sandbox Code Playgroud)
我不知道我的代码有什么问题.
任何帮助,将不胜感激.
这是我的代码:
SamplingThread::SamplingThread( QObject *parent):
QwtSamplingThread( parent ),
d_frequency( 5.0 )
{
init();
}
MainWindow::MainWindow( QWidget *parent ):
QMainWindow( parent )
{.......
.....
run= new QPushButton ("Run",this);
stop= new QPushButton("Stop",this);
connect(run, SIGNAL(clicked()),this, SLOT (start()));
}
MainWindow::start
{
SamplingThread samplingThread;
samplingThread.setFrequency( frequency() );
samplingThread.start();
}
int main( int argc, char **argv )
{
QApplication app( argc, …Run Code Online (Sandbox Code Playgroud) 我有一节课:
class centralDataPool : public QObject
{
Q_OBJECT
public:
centralDataPool(QObject * parent = 0);
~centralDataPool();
commMonitor commOverWatch;
private:
QThread monitorThread;
int totalNum;
signals:
void createMonitor(int);
};
Run Code Online (Sandbox Code Playgroud)
在它的构造函数中我做了:
centralDataPool::centralDataPool(QObject* parent) : QObject(parent),totalNum(0)
{
connect(this, SIGNAL(createMonitor(int)), &commOverWatch, SLOT(createMonitor(int)));
commOverWatch.moveToThread(&monitorThread);
monitorThread.start();
}
Run Code Online (Sandbox Code Playgroud)
当我调用此类的析构函数时,我收到错误消息:
qthread destroyed while thread is still running
Run Code Online (Sandbox Code Playgroud)
但是当我试图在类centralDataPool的析构函数中终止monitorThread时,
centralDataPool::~centralDataPool()
{
monitorThread.terminate();
}
Run Code Online (Sandbox Code Playgroud)
我得到内存泄漏.
在销毁其所有者对象期间终止线程的正确方法是什么?
我实现了一个类,它可以通过QQueue将数据写入串口,并通过插槽读取.我使用QAsyncSerial来反过来使用boost :: asio和回调.该类被移动到一个线程,当QThread发出"started()"时,它的start()方法被执行
问题是我使用forever {}和QWaitCondition在start() - 方法中使QQueue出列.当这个运行时(显然会永远运行),连接到QAsyncSerial的dataReceived信号的插槽无法被调用,因此我从未从串口读取任何内容.
解决这个问题的常用方法是什么?
SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QObject(parent), serialPort(serialPort)
{
m_enqueueMessageMutex = new QMutex();
m_messageQueue = new QQueue<BaseMessage*>();
m_waitCondition = new QWaitCondition();
serial.open(serialPort.deviceName(), 2400);
connect(&serial, SIGNAL(dataReceived(QByteArray)), this, SLOT(serialSlotReceivedData(QByteArray)));
}
void SerialPortHandler::serialSlotReceivedData(QByteArray line)
{
qDebug() << QString(line).toAscii();
}
void SerialPortHandler::sendTestPing()
{
PingMessage *msg = new PingMessage();
enqueueMessage(msg);
}
void SerialPortHandler::enqueueMessage(BaseMessage *msg)
{
QMutexLocker locker(m_enqueueMessageMutex);
m_messageQueue->enqueue(msg);
m_waitCondition->wakeAll();
}
void SerialPortHandler::start()
{
if (!serial.isOpen())
return;
forever {
m_enqueueMessageMutex->lock();
if (m_messageQueue->isEmpty())
m_waitCondition->wait(m_enqueueMessageMutex);
BaseMessage *msg = m_messageQueue->dequeue();
serial.write(msg->encodeForWriting());
m_enqueueMessageMutex->unlock(); …Run Code Online (Sandbox Code Playgroud) 在这里提出这个问题之前,我已经想了很多并阅读了很多文章.这些文章都没有给我一个正确的答案.
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
Run Code Online (Sandbox Code Playgroud)
Worker对象具有新线程的亲和力.
1> Worker完成信号将调用线程上的quit().这将结束线程的事件循环并启动线程完成信号.
2> Worker完成信号连接到worker deleteLater().根据deleteLater()文档
**安排此对象删除.当控制返回到事件循环时,将删除该对象.如果事件循环>未运行
当调用此函数时(例如,在QCoreApplication :: exec()之前在对象上调用deleteLater()),一旦启动事件循环,该对象将被删除.
注意,进入和离开新的事件循环(例如,通过打开模态对话框)将不执行延迟删除; 对于要删除的对象,控件必须返回到调用deleteLater()的事件循环.
注意:不止一次调用此函数是安全的; 在传递第一个延迟删除事件时,将从事件队列中删除该对象的任何挂起事件.**
因此,当没有eventloop时,由于线程已经退出并且它已经提升了完成的信号,我们将不再再次启动相同的线程.在这种情况下,将永远不会处理deleteLater(),因为事件循环不存在,并且根本不会删除worker对象.这不会造成内存泄漏吗?
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
Run Code Online (Sandbox Code Playgroud)
如果我们认为交换这两行将解决问题,那么我还有另外一个问题.QT明确指出,在发出信号时调用插槽的顺序是不确定的
上面提到的文章链接中有很多评论.即便是作者也无法完全回答这个问题
有没有办法强制主线程等到从它创建的所有线程完成他们的工作,然后才能完成程序.我的意思是:
int main(){
QthreadClass a; // in cons' a thread is created and running
QthreadClass b; // same as before
*** wish to wait till both created thread finished their jobs ***
return 0;
Run Code Online (Sandbox Code Playgroud)
}
我正在尝试理解Qt的整个内部过程以及当我使用不同的线程时它是如何工作的.
正如我所理解的(谷歌搜索和探索Qt源代码),如下:
exec与该列表交互的本地事件循环(如果我调用).QCoreApplication::postEvent(obj, e)将该对添加到(obj, e)该obj线程的"待处理事件列表"中.QAbstractEventDispatcherspecializations),其目的是读取系统事件.因此,对于不同的平台,它存在a QEventDispatchWin,a QEventDispatchUnix,a QEventDispatchSymbian等.对于gui事件,Qt也有QEventDispatchX11(继承自QEventDispatchUnix),S60(来自Symbian)等.考虑到所有这些,exec呼叫的工作原理如下:
Thread's `exec`:
? create a QEventLoop object.
? call QEventLoop.exec()
? call repeatedly eventDispatcher's processEvents with WaitForMoreEvents flag.
? call to QCoreApplication::sendPostedEvents
? while (!pending system events)
? ? read system event
? ? create an appropiate QEvent e and detect its target QObject o. …Run Code Online (Sandbox Code Playgroud) 在我的Qt应用程序中,我创建了一个QThread应该定期执行一些繁重的计算任务.主QApplication线程应该维护GUI(不包括在示例中)并执行一些定期更新.两个线程都有自己的定时器来启用常规update()调用.
问题:当工作线程的计算工作负载超过某个临界值时,我的主线程停止接收计时器事件.
示例代码如下.当主线程调用update()时,它输出"Main",工作线程调用"Worker".如果你运行它,你会看到"工人"定期打印,"主"正好出现两次(一个在开头,一个在约5秒).在全功能GUI应用程序的情况下,这实际上意味着完全GUI冻结.
一些观察.
所以,你可以看到我有几个解决方法,但我很感激任何人解释我的原始代码有什么问题.我希望线程能够独立地执行它们的事件循环.我知道我通过一个很长的update()操作来阻止工作线程事件循环,但为什么它在世界上会影响主线程呢?
PS是的,我知道QConcurrent替代方案.但我只是想明白.
#include <windows.h>
#include <QApplication>
#include "test.h"
HANDLE mainThread_ = INVALID_HANDLE_VALUE;
QApplication *app_ = 0;
MyObj *obj_ = 0;
MyThread *thread_ = 0;
MyObj::MyObj()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}
void MyObj::update()
{
printf("Main\n");
}
void MyThread::run()
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
exec();
}
void MyThread::update()
{
printf("Worker\n");
// do some …Run Code Online (Sandbox Code Playgroud)