精华内容
下载资源
问答
  • 现在Qt官方并不是很推荐继承QThread来实现多线程方法,而是极力推崇继承QObject的方法来实现,当然用哪个方法实现要视情况而定,别弄错了就行,估计Qt如此推崇继承QObject的方法可能是QThread太容易用错的原因。...

    现在Qt官方并不是很推荐继承QThread来实现多线程方法,而是极力推崇继承QObject的方法来实现,当然用哪个方法实现要视情况而定,别弄错了就行,估计Qt如此推崇继承QObject的方法可能是QThread太容易用错的原因。

    继承QThread实现多线程的方法点此

    1. 前言

    上一篇介绍了传统的多线程使用方法——继承QThread来实现多线程,这也是很多框架的做法(MFC),但Qt还有一种多线程的实现方法,比直接继承QThread更为灵活,就是直接继承QObject实现多线程。

    QObject是Qt框架的基本类,但凡涉及到信号槽有关的类都是继承于QObjectQObject是一个功能异常强大的类,它提供了Qt关键技术信号和槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject是可以选择不同的线程里执行的。

    QObject的线程转移函数是:void moveToThread(QThread * targetThread) ,通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程里。

    QThread非常容易被新手误用,主要是QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中,此问题在上一篇重点描述了。由于QThread的这个特性,导致在调用QThread的非run函数容易在旧线程中执行,因此人们发现了一个新的魔改QThread的方法: 
    人们发现,咦,QThread也继承QObjectQObject有个函数void moveToThread(QThread * targetThread)可以把Object的运行线程转移,那么:(下面是非常不推荐的魔改做法,别用此方法):

    1. class MyThread : public QThread{
    2. public:
    3. MyThread ()
    4. {
    5. moveToThread(this);
    6. }
    7. ……
    8. };

    直接把MyThread整个转移到MyThread的新线程中,MyThread不仅run,其它函数也在新线程里了。这样的确可以运行正常,但这并不是QThread设计的初衷,Qt还专门发过一篇文章来吐槽这个做法。

    在Qt4.8之后,Qt多线程的写法最好还是通过QObject来实现,和线程的交互通过信号和槽(实际上其实是通过事件)联系。

    2.继承QObject的多线程实现

    QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimerQTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程。

    2.1 创建及销毁线程

    继承QObject多线程的方法线程的创建很简单,只要让QThreadstart函数运行起来就行,但是需要注意销毁线程的方法 
    在线程创建之后,这个QObject的销毁不应该在主线程里进行,而是通过deleteLater槽进行安全的销毁,因此,继承QObject多线程的方法在创建时有几个槽函数需要特别关注:

    • 一个是QThreadfinished信号对接QObjectdeleteLater使得线程结束后,继承QObject的那个多线程类会自己销毁
    • 另一个是QThreadfinished信号对接QThread自己的deleteLater,这个不是必须,下面官方例子就没这样做

    看看Qt官方文档的例子:

    1. class Worker : public QObject
    2. {
    3. Q_OBJECT
    4. public slots:
    5. void doWork(const QString &parameter) {
    6. QString result;
    7. /* ... here is the expensive or blocking operation ... */
    8. emit resultReady(result);
    9. }
    10. signals:
    11. void resultReady(const QString &result);
    12. };
    13. class Controller : public QObject
    14. {
    15. Q_OBJECT
    16. QThread workerThread;
    17. public:
    18. Controller() {
    19. Worker *worker = new Worker;
    20. worker->moveToThread(&workerThread);
    21. connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    22. connect(this, &Controller::operate, worker, &Worker::doWork);
    23. connect(worker, &Worker::resultReady, this, &Controller::handleResults);
    24. workerThread.start();
    25. }
    26. ~Controller() {
    27. workerThread.quit();
    28. workerThread.wait();
    29. }
    30. public slots:
    31. void handleResults(const QString &);
    32. signals:
    33. void operate(const QString &);
    34. };

    使用QObject创建多线程的方法如下:

    • 写一个继承QObject的类,对需要进行复杂耗时逻辑的入口函数声明为槽函数
    • 此类在旧线程new出来,不能给它设置任何父对象
    • 同时声明一个QThread对象,在官方例子里,QThread并没有new出来,这样在析构时就需要调用QThread::wait(),如果是堆分配的话, 可以通过deleteLater来让线程自杀
    • 把obj通过moveToThread方法转移到新线程中,此时object已经是在线程中了
    • 把线程的finished信号和object的deleteLater槽连接,这个信号槽必须连接,否则会内存泄漏
    • 正常连接其他信号和槽(在连接信号槽之前调用moveToThread,不需要处理connect的第五个参数,否则就显示声明用Qt::QueuedConnection来连接
    • 初始化完后调用'QThread::start()'来启动线程
    • 在逻辑结束后,调用QThread::quit退出线程的事件循环

    使用QObject来实现多线程比用继承QThread的方法更加灵活,整个类都是在新的线程中,通过信号槽和主线程传递数据,前篇文章的例子用继承QObject的方法实现的话,代码如下: 
    头文件(ThreadObject.h):

    1. #include <QObject>
    2. #include <QMutex>
    3. class ThreadObject : public QObject
    4. {
    5. Q_OBJECT
    6. public:
    7. ThreadObject(QObject* parent = NULL);
    8. ~ThreadObject();
    9. void setRunCount(int count);
    10. void stop();
    11. signals:
    12. void message(const QString& info);
    13. void progress(int present);
    14. public slots:
    15. void runSomeBigWork1();
    16. void runSomeBigWork2();
    17. private:
    18. int m_runCount;
    19. int m_runCount2;
    20. bool m_isStop;
    21. QMutex m_stopMutex;
    22. };

    cpp文件(ThreadObject.cpp):

    1. #include "ThreadObject.h"
    2. #include <QThread>
    3. #include <QDebug>
    4. #include <QMutexLocker>
    5. #include <QElapsedTimer>
    6. #include <limits>
    7. ThreadObject::ThreadObject(QObject *parent):QObject(parent)
    8. ,m_runCount(10)
    9. ,m_runCount2(std::numeric_limits<int>::max())
    10. ,m_isStop(true)
    11. {
    12. }
    13. ThreadObject::~ThreadObject()
    14. {
    15. qDebug() << "ThreadObject destroy";
    16. emit message(QString("Destroy %1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    17. }
    18. void ThreadObject::setRunCount(int count)
    19. {
    20. m_runCount = count;
    21. emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    22. }
    23. void ThreadObject::runSomeBigWork1()
    24. {
    25. {
    26. QMutexLocker locker(&m_stopMutex);
    27. m_isStop = false;
    28. }
    29. int count = 0;
    30. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    31. emit message(str);
    32. int process = 0;
    33. while(1)
    34. {
    35. {
    36. QMutexLocker locker(&m_stopMutex);
    37. if(m_isStop)
    38. return;
    39. }
    40. if(m_runCount == count)
    41. {
    42. break;
    43. }
    44. sleep(1);
    45. int pro = ((float)count / m_runCount) * 100;
    46. if(pro != process)
    47. {
    48. process = pro;
    49. emit progress(((float)count / m_runCount) * 100);
    50. emit message(QString("Object::run times:%1,m_runCount:%2").arg(count).arg(m_runCount2));
    51. }
    52. ++count;
    53. }
    54. }
    55. void ThreadObject::runSomeBigWork2()
    56. {
    57. {
    58. QMutexLocker locker(&m_stopMutex);
    59. m_isStop = false;
    60. }
    61. int count = 0;
    62. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    63. emit message(str);
    64. int process = 0;
    65. QElapsedTimer timer;
    66. timer.start();
    67. while(1)
    68. {
    69. {
    70. QMutexLocker locker(&m_stopMutex);
    71. if(m_isStop)
    72. return;
    73. }
    74. if(m_runCount2 == count)
    75. {
    76. break;
    77. }
    78. int pro = ((float)count / m_runCount2) * 100;
    79. if(pro != process)
    80. {
    81. process = pro;
    82. emit progress(pro);
    83. emit message(QString("%1,%2,%3,%4")
    84. .arg(count)
    85. .arg(m_runCount2)
    86. .arg(pro)
    87. .arg(timer.elapsed()));
    88. timer.restart();
    89. }
    90. ++count;
    91. }
    92. }
    93. void ThreadObject::stop()
    94. {
    95. QMutexLocker locker(&m_stopMutex);
    96. emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    97. m_isStop = true;
    98. }

    这个Object有两个耗时函数work1和work2,这两个耗时函数的调用都是通过槽函数触发,同时为了能及时打断线程,添加了一个stop函数,stop函数不是通过信号槽触发,因此需要对数据进行保护,这里用了互斥锁对一个bool变量进行了保护处理,当然会失去一些性能。

    主界面的头文件(截取部分代码):

    1. #include <QWidget>
    2. #include <QTimer>
    3. class ThreadFromQThread;
    4. class ThreadObject;
    5. namespace Ui {
    6. class Widget;
    7. }
    8. class Widget : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit Widget(QWidget *parent = 0);
    13. ~Widget();
    14. signals:
    15. void startObjThreadWork1();
    16. void startObjThreadWork2();
    17. private slots:
    18. ……
    19. void onButtonObjectMove2ThreadClicked();
    20. void onButtonObjectMove2Thread2Clicked();
    21. void onButtonObjectQuitClicked();
    22. void onButtonObjectThreadStopClicked();
    23. void progress(int val);
    24. void receiveMessage(const QString& str);
    25. void heartTimeOut();
    26. private
    27. void startObjThread();
    28. private:
    29. Ui::Widget *ui;
    30. ……
    31. ThreadObject* m_obj;
    32. QThread* m_objThread;
    33. };

    cpp文件

    1. Widget::~Widget()
    2. {
    3. qDebug() << "start destroy widget";
    4. if(m_objThread)
    5. {
    6. m_objThread->quit();
    7. }
    8. m_objThread->wait();
    9. qDebug() << "end destroy widget";
    10. }
    11. //创建线程
    12. void Widget::startObjThread()
    13. {
    14. if(m_objThread)
    15. {
    16. return;
    17. }
    18. m_objThread= new QThread();
    19. m_obj = new ThreadObject();
    20. m_obj->moveToThread(m_objThread);
    21. connect(m_objThread,&QThread::finished,m_objThread,&QObject::deleteLater);
    22. connect(m_objThread,&QThread::finished,m_obj,&QObject::deleteLater);
    23. connect(this,&Widget::startObjThreadWork1,m_obj,&ThreadObject::runSomeBigWork1);
    24. connect(this,&Widget::startObjThreadWork2,m_obj,&ThreadObject::runSomeBigWork2);
    25. connect(m_obj,&ThreadObject::progress,this,&Widget::progress);
    26. connect(m_obj,&ThreadObject::message,this,&Widget::receiveMessage);
    27. m_objThread->start();
    28. }
    29. //调用线程的runSomeBigWork1
    30. void Widget::onButtonObjectMove2ThreadClicked()
    31. {
    32. if(!m_objThread)
    33. {
    34. startObjThread();
    35. }
    36. emit startObjThreadWork1();//主线程通过信号换起子线程的槽函数
    37. ui->textBrowser->append("start Obj Thread work 1");
    38. }
    39. //调用线程的runSomeBigWork2
    40. void Widget::onButtonObjectMove2Thread2Clicked()
    41. {
    42. if(!m_objThread)
    43. {
    44. startObjThread();
    45. }
    46. emit startObjThreadWork2();//主线程通过信号换起子线程的槽函数
    47. ui->textBrowser->append("start Obj Thread work 2");
    48. }
    49. //调用线程的中断
    50. void Widget::onButtonObjectThreadStopClicked()
    51. {
    52. if(m_objThread)
    53. {
    54. if(m_obj)
    55. {
    56. m_obj->stop();
    57. }
    58. }
    59. }

    创建线程和官方例子差不多,区别是QThread也是用堆分配,这样,让QThread自杀的槽就一定记得加上,否则QThread就逍遥法外了。

    1. connect(m_objThread,&QThread::finished,m_objThread,&QObject::deleteLater);

    3.加锁对性能的影响

    上例的runSomeBigWork2中,让一个int不停自加1,一直加到int的最大值,为了验证加锁和不加锁的影响,这里对加锁和不加锁运行了两次观察耗时的变化

    1. void ThreadObject::runSomeBigWork2()
    2. {
    3. {
    4. QMutexLocker locker(&m_stopMutex);
    5. m_isStop = false;
    6. }
    7. int count = 0;
    8. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    9. emit message(str);
    10. int process = 0;
    11. QElapsedTimer timer;
    12. timer.start();
    13. while(1)
    14. {
    15. {
    16. QMutexLocker locker(&m_stopMutex);
    17. if(m_isStop)
    18. return;
    19. }
    20. if(m_runCount2 == count)
    21. {
    22. break;
    23. }
    24. int pro = ((float)count / m_runCount2) * 100;
    25. if(pro != process)
    26. {
    27. process = pro;
    28. emit progress(pro);
    29. emit message(QString("%1,%2,%3,%4")
    30. .arg(count)
    31. .arg(m_runCount2)
    32. .arg(pro)
    33. .arg(timer.elapsed()));
    34. timer.restart();
    35. }
    36. ++count;
    37. }
    38. }

    结果如下: 




    这里没个横坐标的每%1进行了21474837次循环,由统计图可见,Debug模式下使用了锁后性能下降4倍,Release模式下下降1.5倍的样子

    # 3.总结

    • 如果线程要用到消息循环,使用继承QObject的多线程方法更简单
    • 继承QObject的多线程不能指定父对象
    • 把所有耗时操作都作为槽函数
    • QMutex会带来一定的耗时,大概速度会降低1.5倍(Release模式)

    –> 见 github


    展开全文
  • 现在Qt官方并不是很推荐继承QThread来实现多线程方法,而是极力推崇继承QObject的方法来实现,当然用哪个方法实现要视情况而定,别弄错了就行,估计Qt如此推崇继承QObject的方法可能是QThread太容易用错的原因。...

    现在Qt官方并不是很推荐继承QThread来实现多线程方法,而是极力推崇继承QObject的方法来实现,当然用哪个方法实现要视情况而定,别弄错了就行,估计Qt如此推崇继承QObject的方法可能是QThread太容易用错的原因。

    继承QThread实现多线程的方法点此

    1. 前言

    上一篇介绍了传统的多线程使用方法——继承QThread来实现多线程,这也是很多框架的做法(MFC),但Qt还有一种多线程的实现方法,比直接继承QThread更为灵活,就是直接继承QObject实现多线程。

    QObject是Qt框架的基本类,但凡涉及到信号槽有关的类都是继承于QObjectQObject是一个功能异常强大的类,它提供了Qt关键技术信号和槽的支持以及事件系统的支持,同时它提供了线程操作的接口,也就是QObject是可以选择不同的线程里执行的。

    QObject的线程转移函数是:void moveToThread(QThread * targetThread) ,通过此函数可以把一个顶层Object(就是没有父级)转移到一个新的线程里。

    QThread非常容易被新手误用,主要是QThread自身并不生存在它run函数所在的线程,而是生存在旧的线程中,此问题在上一篇重点描述了。由于QThread的这个特性,导致在调用QThread的非run函数容易在旧线程中执行,因此人们发现了一个新的魔改QThread的方法: 
    人们发现,咦,QThread也继承QObjectQObject有个函数void moveToThread(QThread * targetThread)可以把Object的运行线程转移,那么:(下面是非常不推荐的魔改做法,别用此方法):

    1. class MyThread : public QThread{
    2. public:
    3. MyThread ()
    4. {
    5. moveToThread(this);
    6. }
    7. ……
    8. };

    直接把MyThread整个转移到MyThread的新线程中,MyThread不仅run,其它函数也在新线程里了。这样的确可以运行正常,但这并不是QThread设计的初衷,Qt还专门发过一篇文章来吐槽这个做法。

    在Qt4.8之后,Qt多线程的写法最好还是通过QObject来实现,和线程的交互通过信号和槽(实际上其实是通过事件)联系。

    2.继承QObject的多线程实现

    QObject来实现多线程有个非常好的优点,就是默认就支持事件循环(Qt的许多非GUI类也需要事件循环支持,如QTimerQTcpSocket),QThread要支持事件循环需要在QThread::run()中调用QThread::exec()来提供对消息循环的支持,否则那些需要事件循环支持的类都不能正常发送信号,因此如果要使用信号和槽,那就直接使用QObject来实现多线程。

    2.1 创建及销毁线程

    继承QObject多线程的方法线程的创建很简单,只要让QThreadstart函数运行起来就行,但是需要注意销毁线程的方法 
    在线程创建之后,这个QObject的销毁不应该在主线程里进行,而是通过deleteLater槽进行安全的销毁,因此,继承QObject多线程的方法在创建时有几个槽函数需要特别关注:

    • 一个是QThreadfinished信号对接QObjectdeleteLater使得线程结束后,继承QObject的那个多线程类会自己销毁
    • 另一个是QThreadfinished信号对接QThread自己的deleteLater,这个不是必须,下面官方例子就没这样做

    看看Qt官方文档的例子:

    1. class Worker : public QObject
    2. {
    3. Q_OBJECT
    4. public slots:
    5. void doWork(const QString &parameter) {
    6. QString result;
    7. /* ... here is the expensive or blocking operation ... */
    8. emit resultReady(result);
    9. }
    10. signals:
    11. void resultReady(const QString &result);
    12. };
    13. class Controller : public QObject
    14. {
    15. Q_OBJECT
    16. QThread workerThread;
    17. public:
    18. Controller() {
    19. Worker *worker = new Worker;
    20. worker->moveToThread(&workerThread);
    21. connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
    22. connect(this, &Controller::operate, worker, &Worker::doWork);
    23. connect(worker, &Worker::resultReady, this, &Controller::handleResults);
    24. workerThread.start();
    25. }
    26. ~Controller() {
    27. workerThread.quit();
    28. workerThread.wait();
    29. }
    30. public slots:
    31. void handleResults(const QString &);
    32. signals:
    33. void operate(const QString &);
    34. };

    使用QObject创建多线程的方法如下:

    • 写一个继承QObject的类,对需要进行复杂耗时逻辑的入口函数声明为槽函数
    • 此类在旧线程new出来,不能给它设置任何父对象
    • 同时声明一个QThread对象,在官方例子里,QThread并没有new出来,这样在析构时就需要调用QThread::wait(),如果是堆分配的话, 可以通过deleteLater来让线程自杀
    • 把obj通过moveToThread方法转移到新线程中,此时object已经是在线程中了
    • 把线程的finished信号和object的deleteLater槽连接,这个信号槽必须连接,否则会内存泄漏
    • 正常连接其他信号和槽(在连接信号槽之前调用moveToThread,不需要处理connect的第五个参数,否则就显示声明用Qt::QueuedConnection来连接
    • 初始化完后调用'QThread::start()'来启动线程
    • 在逻辑结束后,调用QThread::quit退出线程的事件循环

    使用QObject来实现多线程比用继承QThread的方法更加灵活,整个类都是在新的线程中,通过信号槽和主线程传递数据,前篇文章的例子用继承QObject的方法实现的话,代码如下: 
    头文件(ThreadObject.h):

    1. #include <QObject>
    2. #include <QMutex>
    3. class ThreadObject : public QObject
    4. {
    5. Q_OBJECT
    6. public:
    7. ThreadObject(QObject* parent = NULL);
    8. ~ThreadObject();
    9. void setRunCount(int count);
    10. void stop();
    11. signals:
    12. void message(const QString& info);
    13. void progress(int present);
    14. public slots:
    15. void runSomeBigWork1();
    16. void runSomeBigWork2();
    17. private:
    18. int m_runCount;
    19. int m_runCount2;
    20. bool m_isStop;
    21. QMutex m_stopMutex;
    22. };

    cpp文件(ThreadObject.cpp):

    1. #include "ThreadObject.h"
    2. #include <QThread>
    3. #include <QDebug>
    4. #include <QMutexLocker>
    5. #include <QElapsedTimer>
    6. #include <limits>
    7. ThreadObject::ThreadObject(QObject *parent):QObject(parent)
    8. ,m_runCount(10)
    9. ,m_runCount2(std::numeric_limits<int>::max())
    10. ,m_isStop(true)
    11. {
    12. }
    13. ThreadObject::~ThreadObject()
    14. {
    15. qDebug() << "ThreadObject destroy";
    16. emit message(QString("Destroy %1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    17. }
    18. void ThreadObject::setRunCount(int count)
    19. {
    20. m_runCount = count;
    21. emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    22. }
    23. void ThreadObject::runSomeBigWork1()
    24. {
    25. {
    26. QMutexLocker locker(&m_stopMutex);
    27. m_isStop = false;
    28. }
    29. int count = 0;
    30. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    31. emit message(str);
    32. int process = 0;
    33. while(1)
    34. {
    35. {
    36. QMutexLocker locker(&m_stopMutex);
    37. if(m_isStop)
    38. return;
    39. }
    40. if(m_runCount == count)
    41. {
    42. break;
    43. }
    44. sleep(1);
    45. int pro = ((float)count / m_runCount) * 100;
    46. if(pro != process)
    47. {
    48. process = pro;
    49. emit progress(((float)count / m_runCount) * 100);
    50. emit message(QString("Object::run times:%1,m_runCount:%2").arg(count).arg(m_runCount2));
    51. }
    52. ++count;
    53. }
    54. }
    55. void ThreadObject::runSomeBigWork2()
    56. {
    57. {
    58. QMutexLocker locker(&m_stopMutex);
    59. m_isStop = false;
    60. }
    61. int count = 0;
    62. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    63. emit message(str);
    64. int process = 0;
    65. QElapsedTimer timer;
    66. timer.start();
    67. while(1)
    68. {
    69. {
    70. QMutexLocker locker(&m_stopMutex);
    71. if(m_isStop)
    72. return;
    73. }
    74. if(m_runCount2 == count)
    75. {
    76. break;
    77. }
    78. int pro = ((float)count / m_runCount2) * 100;
    79. if(pro != process)
    80. {
    81. process = pro;
    82. emit progress(pro);
    83. emit message(QString("%1,%2,%3,%4")
    84. .arg(count)
    85. .arg(m_runCount2)
    86. .arg(pro)
    87. .arg(timer.elapsed()));
    88. timer.restart();
    89. }
    90. ++count;
    91. }
    92. }
    93. void ThreadObject::stop()
    94. {
    95. QMutexLocker locker(&m_stopMutex);
    96. emit message(QString("%1->%2,thread id:%3").arg(__FUNCTION__).arg(__FILE__).arg((int)QThread::currentThreadId()));
    97. m_isStop = true;
    98. }

    这个Object有两个耗时函数work1和work2,这两个耗时函数的调用都是通过槽函数触发,同时为了能及时打断线程,添加了一个stop函数,stop函数不是通过信号槽触发,因此需要对数据进行保护,这里用了互斥锁对一个bool变量进行了保护处理,当然会失去一些性能。

    主界面的头文件(截取部分代码):

    1. #include <QWidget>
    2. #include <QTimer>
    3. class ThreadFromQThread;
    4. class ThreadObject;
    5. namespace Ui {
    6. class Widget;
    7. }
    8. class Widget : public QWidget
    9. {
    10. Q_OBJECT
    11. public:
    12. explicit Widget(QWidget *parent = 0);
    13. ~Widget();
    14. signals:
    15. void startObjThreadWork1();
    16. void startObjThreadWork2();
    17. private slots:
    18. ……
    19. void onButtonObjectMove2ThreadClicked();
    20. void onButtonObjectMove2Thread2Clicked();
    21. void onButtonObjectQuitClicked();
    22. void onButtonObjectThreadStopClicked();
    23. void progress(int val);
    24. void receiveMessage(const QString& str);
    25. void heartTimeOut();
    26. private
    27. void startObjThread();
    28. private:
    29. Ui::Widget *ui;
    30. ……
    31. ThreadObject* m_obj;
    32. QThread* m_objThread;
    33. };

    cpp文件

    1. Widget::~Widget()
    2. {
    3. qDebug() << "start destroy widget";
    4. if(m_objThread)
    5. {
    6. m_objThread->quit();
    7. }
    8. m_objThread->wait();
    9. qDebug() << "end destroy widget";
    10. }
    11. //创建线程
    12. void Widget::startObjThread()
    13. {
    14. if(m_objThread)
    15. {
    16. return;
    17. }
    18. m_objThread= new QThread();
    19. m_obj = new ThreadObject();
    20. m_obj->moveToThread(m_objThread);
    21. connect(m_objThread,&QThread::finished,m_objThread,&QObject::deleteLater);
    22. connect(m_objThread,&QThread::finished,m_obj,&QObject::deleteLater);
    23. connect(this,&Widget::startObjThreadWork1,m_obj,&ThreadObject::runSomeBigWork1);
    24. connect(this,&Widget::startObjThreadWork2,m_obj,&ThreadObject::runSomeBigWork2);
    25. connect(m_obj,&ThreadObject::progress,this,&Widget::progress);
    26. connect(m_obj,&ThreadObject::message,this,&Widget::receiveMessage);
    27. m_objThread->start();
    28. }
    29. //调用线程的runSomeBigWork1
    30. void Widget::onButtonObjectMove2ThreadClicked()
    31. {
    32. if(!m_objThread)
    33. {
    34. startObjThread();
    35. }
    36. emit startObjThreadWork1();//主线程通过信号换起子线程的槽函数
    37. ui->textBrowser->append("start Obj Thread work 1");
    38. }
    39. //调用线程的runSomeBigWork2
    40. void Widget::onButtonObjectMove2Thread2Clicked()
    41. {
    42. if(!m_objThread)
    43. {
    44. startObjThread();
    45. }
    46. emit startObjThreadWork2();//主线程通过信号换起子线程的槽函数
    47. ui->textBrowser->append("start Obj Thread work 2");
    48. }
    49. //调用线程的中断
    50. void Widget::onButtonObjectThreadStopClicked()
    51. {
    52. if(m_objThread)
    53. {
    54. if(m_obj)
    55. {
    56. m_obj->stop();
    57. }
    58. }
    59. }

    创建线程和官方例子差不多,区别是QThread也是用堆分配,这样,让QThread自杀的槽就一定记得加上,否则QThread就逍遥法外了。

    1. connect(m_objThread,&QThread::finished,m_objThread,&QObject::deleteLater);

    3.加锁对性能的影响

    上例的runSomeBigWork2中,让一个int不停自加1,一直加到int的最大值,为了验证加锁和不加锁的影响,这里对加锁和不加锁运行了两次观察耗时的变化

    1. void ThreadObject::runSomeBigWork2()
    2. {
    3. {
    4. QMutexLocker locker(&m_stopMutex);
    5. m_isStop = false;
    6. }
    7. int count = 0;
    8. QString str = QString("%1->%2,thread id:%3").arg(__FILE__).arg(__FUNCTION__).arg((int)QThread::currentThreadId());
    9. emit message(str);
    10. int process = 0;
    11. QElapsedTimer timer;
    12. timer.start();
    13. while(1)
    14. {
    15. {
    16. QMutexLocker locker(&m_stopMutex);
    17. if(m_isStop)
    18. return;
    19. }
    20. if(m_runCount2 == count)
    21. {
    22. break;
    23. }
    24. int pro = ((float)count / m_runCount2) * 100;
    25. if(pro != process)
    26. {
    27. process = pro;
    28. emit progress(pro);
    29. emit message(QString("%1,%2,%3,%4")
    30. .arg(count)
    31. .arg(m_runCount2)
    32. .arg(pro)
    33. .arg(timer.elapsed()));
    34. timer.restart();
    35. }
    36. ++count;
    37. }
    38. }

    结果如下: 




    这里没个横坐标的每%1进行了21474837次循环,由统计图可见,Debug模式下使用了锁后性能下降4倍,Release模式下下降1.5倍的样子

    # 3.总结

    • 如果线程要用到消息循环,使用继承QObject的多线程方法更简单
    • 继承QObject的多线程不能指定父对象
    • 把所有耗时操作都作为槽函数
    • QMutex会带来一定的耗时,大概速度会降低1.5倍(Release模式)

    –> 见 github


    展开全文
  • Thread.sleep(3000);就是指让当前正在运行占用cpu时间片...是帮助所有线程获得运行时间最佳方法需要注意是就算线程睡眠时间到了,他也不是立即会被运行,只是从睡眠状态变为了可运行状态,是不会由睡眠...

    Thread.sleep(3000);

    就是指让当前正在运行的占用cpu时间片的线程挂起3000ms,把cpu的时间片交给其他线程,但是并没有指定把CPU的时间片接下来到底交给哪个线程,而是让这些线程自己去竞争(一般操作系统会根据优先级调度)

    所以说让当线程睡眠,是帮助所有线程获得运行时间的最佳方法

    需要的注意的是就算线程的睡眠时间到了,他也不是立即会被运行,只是从睡眠状态变为了可运行状态,是不会由睡眠状态直接变为运行状态的

    下面举一个例子

    乌龟和兔子赛跑:Call.java

    package 多线程;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    import org.omg.CORBA.INTERNAL;
    
    /**
     * @author:yb
     * @version 创建时间:2018-12-25 下午8:07:11 类说明
     */
    public class Call {
        /*
         * 使用Callable创造线程
         */
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            // 创建线程池
            ExecutorService service = Executors.newFixedThreadPool(2);
            Race tortoise = new Race("乌龟",1000);
            Race rabbit = new Race("兔子", 500);
            
            Future<Integer> result1 = service.submit(tortoise);
            Future<Integer> result2 = service.submit(rabbit);
            
            Thread.sleep(3000);//主线程挂起3000ms 乌龟和兔子线程开始竞争cpu 即乌龟和兔子开始跑,跑的时间都是3000ms
            tortoise.setFlag(false);
            rabbit.setFlag(false);
             
            //获取值
            int num1=result1.get();
            System.out.println("乌龟跑了"+num1+"步");
            int num2=result2.get();
            System.out.println("兔子跑了"+num2+"步");
            
            //停止服务
            service.shutdownNow();
            
        }
    
    }
    
    class Race implements Callable<Integer>{
        
        private String name;//名称
        private int time;//延时
        private int step=0;//步数
        
        public Race(String name,int time) {
            super();
            this.name=name;
            this.time=time;
        }
        
        private boolean flag= true;
        public int getTime() {
            return time;
        }
    
        public void setTime(int time) {
            this.time = time;
        }
    
        public boolean isFlag() {
            return flag;
        }
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    
        public int getStep() {
            return step;
        }
    
        public void setStep(int step) {
            this.step = step;
        }
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer call() throws Exception{
        
            while(flag) {
                Thread.sleep(time);
                step++;    
            }
            return step;
        }
    }


    分析:

    main函数也是一个线程,运行main函数的时候,Thread.sleep(3000);就是让main函数线程挂起3000ms,所以这个3000ms是乌龟和兔子的比赛总时间,main函数线程挂起之后,时间片交给了乌龟和兔子线程让我们去竞争占用cpu时间片



    转载于:https://www.cnblogs.com/yinbiao/p/10179563.html

    展开全文
  • 为什么在Object类而不是Thread类中声明wait()和notify()方法?9个解决方案42 votes因为,您等待给定对象(或特定地,其监视器)使用此功能。我认为您可能会误解了这些方法的工作方式。 它们不只是处于线程粒度级别,...

    多线程-为什么在Java的Object类中声明wait()和notify()?

    为什么在Object类而不是Thread类中声明wait()和notify()方法?

    9个解决方案

    42 votes

    因为,您等待给定的对象(或特定地,其监视器)使用此功能。

    我认为您可能会误解了这些方法的工作方式。 它们不只是处于线程粒度级别,即不是仅调用wait()并在下一次调用notify()时被唤醒的情况。相反,您始终在特定对象上调用notify(),并且只会被唤醒 在该对象上调用myObj.wait()。

    这样做很好,因为否则并发原语就无法扩展。 这将等效于具有全局名称空间,因为在程序中任何位置对notify()的任何调用都可能使任何并发代码混乱,因为它们将唤醒myObj.wait()调用中阻塞的所有线程。 因此,您在特定对象上调用它们的原因; 它为等待通知对提供了运行的上下文,因此,当您在私有对象上调用myObj.notify()时,可以确保只唤醒在类中调用了wait方法的线程。 某些正在等待另一个对象的Spring线程不会被此调用唤醒,反之亦然。

    编辑:或者从另一个角度解决它-我希望从您的问题中您认为您将获得等待线程的句柄,并在该线程上调用notify()将其唤醒。 不这样做的原因是,您将不得不自己做很多家务。 要等待的线程将不得不在其他线程可以看到的地方发布对自身的引用。 必须正确同步它以增强一致性和可见性。 而且,当您想唤醒线程时,必须掌握此引用,将其唤醒,然后从任何读取它的位置将其删除。 与仅在睡眠线程中调用myObj.wait(),然后在唤醒线程中调用myObj.notify()相比,涉及更多的手动脚手架,并且出错的可能性更大(尤其是在并发环境中)。

    Andrzej Doyle answered 2020-01-10T18:55:47Z

    10 votes

    最简单,最明显的原因是任何对象(不仅仅是线程)可以是线程的监视器。 等待和通知在监控。 正在运行的线程与监视器进行检查。 所以wait和notify方法在Object而不是Thread中

    valli answered 2020-01-10T18:56:08Z

    8 votes

    因为一次只有一个线程可以拥有一个对象的监视器,而该监视器就是线程正在等待或通知的对象。 如果您阅读了Object.notify()和Object.wait()的javadoc,则会对其进行详细说明。

    Taylor Leese answered 2020-01-10T18:56:28Z

    1 votes

    同步机制涉及一个概念-监视对象。 调用wait()时,将请求监视器,并中止进一步执行,直到获取监视器或发生InterruptedException。 调用notify()时,将释放监视器。

    让我们设想一下,如果将wait()和notify()放在Thread类而不是Object类中。 在代码中的一点上,调用currentThread,然后访问对象foo()。

    //.........

    currentThread.wait();

    anObject.setValue(1);

    //.........

    调用currentThread.wait()时,将请求currentThread的监视器,并且直到获取该监视器或发生InterruptedException为止,都不会进一步执行。 现在,在等待状态下,如果从另一个线程调用驻留在currentThread中的另一个对象anotherObject的方法foo(),即使调用的方法foo()不访问anObject,该方法也会被卡住。如果在anObject上调用了第一个wait()方法,则 对于线程本身,驻留在同一线程中的对象上的其他方法调用(不访问anObject)不会卡住。

    因此,在Object类(或其子类)上调用wait()和notify()方法可提供更大的并发性,这就是为什么这些方法在Object类而不是Thread类中的原因。

    Programmer in Paradise answered 2020-01-10T18:57:03Z

    1 votes

    其他一些答案使用“监视”一词,但没有一个解释它的含义。

    “监视程序”这个名称最早是在1970年代创造的,它所指的对象具有其自身的固有锁定以及相关的等待/通知机制。 [https://en.wikipedia.org/wiki/Monitor_%28synchronization%29]

    二十年后的短时间内,台式机,多处理器计算机的出现是一种新颖的现象,时髦的想法是为它们设计软件的正确方法是创建面向对象的程序,其中每个对象都是一个对象。 监控。

    事实证明这并不是一个有用的想法,但是那一刻恰好是发明Java编程语言的那一刻。

    Solomon Slow answered 2020-01-10T18:57:37Z

    0 votes

    请在此处阅读有关等待通知的说明。

    但是最好在应用程序中避免这些,并使用更新的java.util.concurrent包。

    kgiannakakis answered 2020-01-10T18:58:01Z

    0 votes

    我将以一种简单的方式进行说明:

    要调用wait()或notify(),您需要拥有对象监视器-这意味着wait()或notify()必须存在于同步块中

    synchronized(monitorObj){

    monitorObj.wait() or even notify

    }

    这就是这些方法出现在对象类中的原因

    Prabakaran Natarajan answered 2020-01-10T18:58:30Z

    0 votes

    这是因为这些方法用于线程间通信,并且线程间通信通过使用锁发生,但是锁与对象相关联。因此它在对象类中。

    Sandeep answered 2020-01-10T18:58:50Z

    0 votes

    Wait和Notify方法用于Java中两个线程之间的通信。 因此,Object类是使它们可用于Java中的每个对象的正确位置。

    另一个原因是每个对象都可以使用锁。 线程需要锁,并且它们等待锁,他们不知道哪个线程持有锁,而只是知道某个线程持有该锁,因此应该等待锁,而不是知道哪个线程位于同步块中,并要求它们释放 锁

    Anil Satija answered 2020-01-10T18:59:15Z

    展开全文
  • Java Thread之Sleep()

    2020-10-30 10:37:56
    sleep()方法Thread静态方法哪个线程调用了这个sleep方法哪个线程就休眠sleep设置时间。线程睡眠到期自动苏醒,并返回到可运行状态(就绪),不是运行状态。 二.sleep使用方法 public class ThreadSleep...
  • android中用到了太多工厂类,其中有用工厂方法模式,当然也有很多工厂并不是使用工厂方法模式,只是工具管理类。 今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式。 工厂方法模式,Factory Method...
  • android中用到了太多工厂类,其中有用工厂方法模式,当然也有很多工厂并不是使用工厂方法模式,只是工具管理类。今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式。工厂方法模式,Factory Method,...
  • java Thread(线程)

    2018-04-06 21:55:04
    1. 下列有关Thread的描述,哪个是正确的? A. 启动一个线程的方法是:thread. run() B. 结束一个线程的通常做法是:thread. stop() C. 将一个线程标记成daemon线程,意味着当主线程结束, 并且没有其它正在运行...
  • // 将Message对象送入到main thread的MessageQueue里面 mHandler.sendMessage(m); break; case R.id.b102: //other线程发送消息给主线程 postRunnable = false; noLooperThread = new NoLooperThread...
  • 系统:Linux场景:应用服务器Server压测,出现CPU飙高,相应很慢目的:快速定位CPU被什么玩意给占用了思路:找到是哪个线程占用CPU最多,找到此线程上什么方法导致此线程占用CPU过多解决方案和步骤:linux服务器环境...
  • JAVA学习线程随笔

    2020-07-30 18:58:20
    程序 进程 线程 多线程实现的两种方式: 1.自定义一个线程类, extends Thread 重写 run 方法 创建自定义的线程对象 开启线程 start() 2.自定义一个任务类, implements Runnable ...线程Thread的构造方法: 1.new 自定义线
  • JAVA面试题详细答案

    千次阅读 2017-10-09 14:51:25
    一 单选题 1. 0.6332的数据类型是() A float B double C Float D Double 答案:B 解析:默认为double型,如果为float型需要加上f显示说明,... 下面哪个不是Thread的方法() A start() B run() C exit() D ge
  • 程序、进程、线程之间区别与联系: 程序:一段静态代码 进程: 静态代码动态执行过程,包括...currentThread()可以返回段正在被哪个线程调用信息 getName() 得到线程名字 getId() 得到线程ID ...
  • sleep是线程类(Thread的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程...
  • api中解释,PrintStream打印流print(int a)方法是通过调用String.valueOf...源码中并不是按照write(byte[] s)写入,而是直接用字符流write(String s)写入,这是为什么啊,该以哪个为准啊,求大神解惑,感谢!
  • 第7章 Java中的方法——给汽车丰富多彩的功能 154 教学视频:2小时55分钟 7.1 方法:让汽车动开动 154 7.1.1 引出问题:开动汽车 154 7.1.2 那么,方法到底是什么呢? 155 7.1.3 方法调用过程初探 156 7.2 ...
  • API(七)

    2020-09-18 20:52:32
    线程: 程序 进程 线程 多线程实现的两种方式: 1.自定义一个线程类, extends Thread 重写 run 方法 创建自定义的线程对象 开启线程 start() 2.自定义一个任务类, implements ...线程Thread的构造方法: 1.new 自定义线
  • 第7章 Java中的方法——给汽车丰富多彩的功能 154 教学视频:2小时55分钟 7.1 方法:让汽车动开动 154 7.1.1 引出问题:开动汽车 154 7.1.2 那么,方法到底是什么呢? 155 7.1.3 方法调用过程初探 156 7.2 ...
  • 第7章 Java中的方法——给汽车丰富多彩的功能 154 教学视频:2小时55分钟 7.1 方法:让汽车动开动 154 7.1.1 引出问题:开动汽车 154 7.1.2 那么,方法到底是什么呢? 155 7.1.3 方法调用过程初探 156 7.2 ...
  • 类Annotation 3个Annotation描述类可预期...@NotThreadSafe:类不是线程安全,如果类未加任何注解,则不能确定是否线程安全,认为是非线程安全。 域Annotation和方法Annotation 描述哪个状态变量被哪个锁...
  • 继承thread类必须实现哪个方法2.关于ArratList和LinkedList说法错误是3.MySQL中语句%和 _ 表达正确是4.下面那个关于接口表述是错误5.下面哪项不是数据库四大特征之一6.以下对封装描述正确是7.以下...
  • 多线程 -- 并发与同步

    2020-06-20 10:16:28
    Thread的构造方法中持有一个Runnable对象 需要传入哪个线程对象在订票,也就是说synchronized同步方法必须放在Web12306这个类,因为购票时需要锁Web12306,而不是锁Passenger。Passenger继承了Thread,是一个代理...
  • java 线程

    2018-03-12 20:36:41
    1. 下列有关Thread的描述,哪个是正确的?正确答案: C A. 启动一个线程的方法是:thread. run() B. 结束一个线程的通常做法是:thread. stop() C. 将一个线程标记成daemon线程,意味着当主线程结束,并且没有...
  • 解析:run方法不是给程序员用,是给编译器用。 3.如让线程休眠2秒钟应调用什么方法?解析:Thread.sleep(2000); 4.什么是线程优先级,它在线程调度中作用?解析:先执行哪个线程,优先级高先被执行。 作用...
  • Java线程优先级基本概念线程优先级取值范围线程优先级相关方法示例代码 基本概念 Java提供一个线程调度器来监控程序中启动后进入就绪状态所有线程,线程调度器按照优先级决定执行哪个线程。 (注意:优先级低...
  • join 和 yield

    2017-03-18 11:23:19
    join 是合并线程。(当线程中使用t.join的意思就是说,执行完当前...yield是暂停线程,是一个静态的方法。 但是 不是一个绝对的暂停,就是一会可能cpu又调度到了。当这个Thread.yield写在哪个线程中,就暂停那个线程。
  • MFC程序框架剖析

    2015-03-05 09:53:19
    们之间纽带仅仅在于这个C++窗口类内部成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 句柄 (3)但是,当C++窗口类对象销毁时,与之相关窗口也将销毁,因为它们之间纽带m_hWnd已经断了 3、...
  • //设置windowicon(这里我自定义了一下Windows窗口icon图标,因为实在觉得哪个小咖啡图标不好看 = =) Toolkit toolKit = getToolkit();//返回此窗体工具包 Image icon = toolKit.getImage(Client.class....
  • java 面试题 总结

    2009-09-16 08:45:34
    最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 Hashtable和HashMap采用的hash/rehash算法都大概...

空空如也

空空如也

1 2 3
收藏数 42
精华内容 16
关键字:

哪个不是thread的方法