GUI操作顺序问题引发异常:

  有时候我们使用写GUI程序的时候会遇到这样的问题:比如在程序中,建立了一个列表的GUI。这个列表是随着时间不断更新的,而且操作也会读取这个列表GUI的内容。
  如果这个程序是多线程的程序,而且只是除了GUI的线程不操作,只是其他线程操作这个列表GUI,那么这个问题很简单,只用加互斥锁就可以了。但如果GUI线程自己本身也要操作这个列表,那么这个问题就很麻烦了。
  我们可以很容易地想到一种场景,比如GUI线程读了列表的一些表项(比如选定),此时线程中的某个方法keep了这些表项的指针,然而此时很不幸别的线程有一个请求需要删除列表中的一些表项,并且这些表项有一些包含在了我们的选定内容里,我们知道几乎所有的语言操作GUI时都要进入GUI线程里面操作,那么我们刚才选定表项的那个方法会被打断,然后进入删除表项方法,在删除了表项以后再次回到选定表项方法时,我们的选定的表项有一些已经被删除了,此时我们再进行操作很有可能不符合我们的要求。
  如果你是用一般是用C#,JAVA这种不用自己管理内存的语言,那还好,只是结果可能不对,但是如果是用C++这种需要我们自己管理内存的来写,很有可能我们会操作一个被释放了内存的对象,然后程序崩掉,这样的情况是我们不想看到的。
 
用事件队列来解决问题:

  下面用一幅图来表示如何设计事件队列:
 
  当然图中虽然是只有三种操作,如果你想,你可以设计出更多的操作,比如读操作,你可以细分为复制表项中的信息和给表项中对应的内容进行操作等。
这样设计以后,就一定程度上消除了GUI打断操作的问题(比如我们会再遇到我们上面的那种访问了一个被析构了的对象问题)。
 
  在Qt中我们可以这样写:(ConnectionView这个对象就是我上面说的那种表项)
  1. class ItemsOpsBase
  2. {
  3. public:
  4. virtual void doOperation(ConnectionView *view) = ;
  5. virtual ~ItemsOpsBase() = default;
  6. };
  7.  
  8. class DeleteItem : public ItemsOpsBase
  9. {
  10. public:
  11. DeleteItem(qintptr target,qint32 connectionIndex)
  12. :ItemsOpsBase(), _target(target),_connectionIndex(connectionIndex){ }
  13.  
  14. void doOperation(ConnectionView *view)override;
  15. ~DeleteItem() = default;
  16. private:
  17. qintptr _target;
  18. qint32 _connectionIndex;
  19. };
  20.  
  21. class UpdatePulse :public ItemsOpsBase
  22. {
  23. public:
  24. UpdatePulse(qintptr descriptor,qint32 currentTime)
  25. :ItemsOpsBase(), _descriptor(descriptor),_currentTime(currentTime){ }
  26.  
  27. void doOperation(ConnectionView *view)override;
  28. ~UpdatePulse() = default;
  29. private:
  30. qintptr _descriptor;
  31. qint32 _currentTime;
  32. };
  33.  
  34. class UpdateRemark : public ItemsOpsBase
  35. {
  36. public:
  37. UpdateRemark(qintptr descriptor, const QString &remark)
  38. : ItemsOpsBase(),_remark(remark),_descriptor(descriptor){ }
  39.  
  40. void doOperation(ConnectionView *view)override;
  41. ~UpdateRemark() = default;
  42. private:
  43. QString _remark;
  44. qintptr _descriptor;
  45. };
  46.  
  47. class TestConnection : public ItemsOpsBase
  48. {
  49. public:
  50. void doOperation(ConnectionView *view)override;
  51. };
  52. class TestConnectionProducer : public QThread
  53. {
  54. public:
  55. void run()override;
  56. };
  57.  
  58. class CopySelectedItemInformProducer : public QThread
  59. {
  60. public:
  61. void run()override;
  62. };
  63.  
  64. class DisconnectTargetsProducer : public QThread
  65. {
  66. public:
  67. void run()override;
  68. };
  69.  
  70. class DeleteItemProducer :public QThread
  71. {
  72. public:
  73. DeleteItemProducer(qintptr target, qint32 connectionIndex)
  74. : QThread(),_target(target),_connectionIndex(connectionIndex) { }
  75. void run()override;
  76. private:
  77. qintptr _target;
  78. qint32 _connectionIndex;
  79. };
  80.  
  81. class UpdatePulseProducer :public QThread
  82. {
  83. public:
  84. UpdatePulseProducer(qintptr descriptor, qint32 currentTime)
  85. :QThread(),_descriptor(descriptor),_currentTime(currentTime){ }
  86. protected:
  87. void run()override;
  88. private:
  89. qintptr _descriptor;
  90. qint32 _currentTime;
  91. };
  92.  
  93. class UpdateRemarkProducer : public QThread
  94. {
  95. public:
  96. UpdateRemarkProducer(qintptr descriptor, const QString &remark)
  97. :QThread(),_remark(remark),_descriptor(descriptor){ }
  98. protected:
  99. void run()override;
  100. private:
  101. QString _remark;
  102. qintptr _descriptor;
  103. };
  104. class ConsumerHelper :public QThread
  105. {
  106. public:
  107. ConsumerHelper(ConnectionView *view)
  108. :QThread(),_view(view){ }
  109. ~ConsumerHelper();
  110. protected:
  111. void run() override;
  112. private:
  113. ConnectionView *_view;
  114.  
  115. ConsumerHelper(const ConsumerHelper &other) = delete;
  116. ConsumerHelper(const ConsumerHelper &&other) = delete;
  117. ConsumerHelper &operator=(const ConsumerHelper &other) = delete;
  118. };
  互斥锁以及队列的代码:
  1. static QQueue<QSharedPointer<ItemsOpsBase>> &opQueue()
  2. {
  3. static QQueue<QSharedPointer<ItemsOpsBase>> queue;
  4. return queue;
  5. }
  6.  
  7. static QSharedPointer<ItemsOpsBase> endOperation;
  8.  
  9. static QMutex &opQueueLock()
  10. {
  11. static QMutex mutex;
  12. return mutex;
  13. }
  14. static QWaitCondition &opQueueIsAvailable()
  15. {
  16. static QWaitCondition flag;
  17. return flag;
  18. }
  ConsumerHelper是一个消费者线程,一直监视着队列的动向,当需要一个某个操作的时候,我们就可以引发一个对象操作的线程,把对应操作加入队列中(为什么需要开一个线程,是为了方便互斥),比如下面我需要一个删除操作:
  删除操作的代码:
  1. void DeleteItem::doOperation(ConnectionView *view)
  2. {
  3. qRegisterMetaType<qintptr>("qintptr");
  4. qRegisterMetaType<TcpConnectionHandler *>("TcpConnectionHandler *");
  5. QMetaObject::invokeMethod(view, "deleteConnection",Qt::QueuedConnection, Q_ARG(qintptr, _target), Q_ARG(qint32, _connectionIndex));
  6. }
  7. void DeleteItemProducer::run()
  8. {
  9. QSharedPointer<ItemsOpsBase> op = QSharedPointer<ItemsOpsBase>(new DeleteItem(_target,_connectionIndex));
  10.  
  11. QMutexLocker locker(&opQueueLock());
  12. opQueue().enqueue(op);
  13. opQueueIsAvailable().wakeOne();
  14. }
 
 
消费者线程的代码:
  1. void ConsumerHelper::run()
  2. {
  3. forever
  4. {
  5. QSharedPointer<ItemsOpsBase> opPointer;
  6.  
  7. {
  8. QMutexLocker locker(&opQueueLock());
  9.  
  10. if (opQueue().isEmpty())
  11. opQueueIsAvailable().wait(&opQueueLock());
  12. opPointer = opQueue().dequeue();
  13.  
  14. if (opPointer == endOperation)
  15. break;
  16. }
  17. {
  18. if(!opPointer.isNull())
  19. opPointer->doOperation(_view);
  20. }
  21. }
  22. }
  23.  
  24. ConsumerHelper::~ConsumerHelper()
  25. {
  26. {
  27. QMutexLocker locker(&opQueueLock());
  28. while(!opQueue().isEmpty())
  29. opQueue().dequeue();
  30.  
  31. opQueue().enqueue(endOperation);
  32. opQueueIsAvailable().wakeOne();
  33. }
  34.  
  35. wait();//注意这里是wait在次线程上的
  36. }
 
  这个时候我只需要在需要用到删除操作的地方用:
  1. DeleteItemProducer *deleteItemProducer = new DeleteItemProducer(target,index);
  2. connect(deleteItemProducer, &QThread::finished, deleteItemProducer, &QThread::deleteLater);
  3. deleteItemProducer->start();
启动删除操作生产者的线程就可以了,我们就把删除操作加入队列中,合适的时候,消费者线程会执行这个操作,并且把操作投递到GUI线程中进行。
 
 
 

用事件队列解决GUI的操作顺序问题(Qt中处理方法)的更多相关文章

  1. Qt中中文字符 一劳永逸的解决方法

    QT中中文字符问题,有没有一劳永逸的解决方法? 目前遇到有以下问题 1.字符串中有中文时,编译提示"常量中含有换行符" 2.在控制台窗口输出中文时无法正常显示,中文全部显示为? 目 ...

  2. Qt 中 Oracle 数据库 QOCI 驱动问题及解决

    Qt 中 Oracle 数据库 QOCI 驱动问题及解决是本文要讲述的问题,用Qt开发Oracle程序时,常会遇到QOCI驱动问题,主要表现为程序运行时出现下面的错误. QOCI driver not ...

  3. 快速解决Ubuntu/linux 环境下QT生成没有可执行文件(application/x-executable)

    快速解决Ubuntu/linux 环境下QT生成没有可执行文件(application/x-executable)(转载)   问题描述 与windows环境下不同,linux选择debug构建时并不 ...

  4. Windows平台下Qt QOpenGL中glutSolidSphere()方法未定义的解决方法

    Windows平台下Qt中glut库的使用     用Qt中的QGLWidget窗体类中是不包括glut工具库的,难怪在myGLWidget(在我的程序中是QGLWidget的派生类)中绘制实心球体是 ...

  5. 【Qt开发】qt中涉及到空格包含路径的解决办法

    qt中涉及到空格路径,qmake是无法正确编译的. 需要在空格路径前面加上$$quote INCLUDEPATH += $$quote(C:/Program Files/MySQL/MySQL Ser ...

  6. QT中QString与string的转化,解决中文乱码问题

    在QT中,使用QString输出到控件进行显示时,经常会出现中文乱码,网上查了一圈,发现大部分都是针对QT4增加4条语句:</span> [cpp] view plain copy QTe ...

  7. Qt入门(9)——Qt中的线程支持

    Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法.警告:所有的GUI类(比如,QWidget和它的子类),操作系统核心 ...

  8. Qt中调用PolarSSL库(一)

    最近一直在学习SSL相关的知识,也是先了解理论相关的知识,主要是SSL相关的基本概念和连接建立过程,主要是基于PolarSSL开源库进行学习.学习完了之后就希望能给有所运用,就想用Qt写一个简单的程序 ...

  9. Qt中Ui名字空间以及setupUi函数的原理和实现 <转>

    用最新的QtCreator选择GUI的应用会产生含有如下文件的工程 下面就简单分析下各部分的功能. .pro文件是供qmake使用的文件,不是本文的重点[不过其实也很简单的],在此不多赘述. 所以呢, ...

随机推荐

  1. U3D手游《苍穹变》性能优化经验谈

    4月11日,由unity公司举办的Unite 2016大会在上海正式举行,在4月12日的案例分享专场会议上,天神互动U3D高级开发工程师康凯以手游<苍穹变>为例讲述了3DMMOARPG游戏 ...

  2. 指向函数的指针和block

    原文网址: http://www.cnblogs.com/cxbblog/p/3841226.html 一:block基础知识 block基础知识 基本概念:block是用来保存一段代码的:^:是bl ...

  3. 学习makefile与autoconfig笔记,持续更新

    main.c #include<stdio.h> #include"chen_print.h" int main(int argc , char * argv ){ c ...

  4. Vue.js实战

    指令 什么是指令 指令,directives,是vue非常常用的功能,在template里. 都是以v-开头 不是自己所为html元素,比如假设指令叫v-abc,没有这种写法,这是组件(compone ...

  5. 企业级应用,如何实现服务化二(dubbo架构)

    这是企业级应用,如何实现服务化系列的第二篇.在上一篇:企业级应用,如何实现服务化一(项目架构演化)中,交代了企业级应用架构的演化过程,和服务治理的方案可以选择:dubbo,或者spring cloud ...

  6. 【Netty】利用Netty实现心跳检测和重连机制

    一.前言 心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制.   我们用到的很多框架都用到了心跳检测,比如服务注册到 Eureka Server 之后会维 ...

  7. properties 文件注意事项

    不要使用""双引号包裹内容 db.validationQuery="select 1"比如上面这种是错误的,下面的是正确的写法 db.validationQue ...

  8. html Css PC 移动端 公用部分样式代码整理

    css常用公用部分样式代码整理: body, html, div, blockquote, img, label, p, h1, h2, h3, h4, h5, h6, pre, ul, ol, li ...

  9. Codeforces 1143B(思维、技巧)

    自己水平太低,不丢人. 结论是最后选取的数后缀一定是若干个9,暴举即可.然而暴举也有暴举的艺术. ll n; ll dfs(ll n) { if (n == 0) return 1; if (n &l ...

  10. [已读]用Angularjs开发下一代web应用

    屯了很久了,貌似是国内出现的第一本讲angularjs的书...上上周看完的时候,angular2都要出来了...angular的双向绑定很赞,因为之前公司后台系统我都用tmodjs做,模板语法什么的 ...