Qt 多线程同步与通信

1 多线程同步

Qt提供了以下几个类来完成这一点:QMutex、QMutexLocker、QSemphore、QWaitCondition。
当然可能还包含QReadWriteLocker、QReadLocker、QWriteLocker,但线程同步是应用很少,这里只做简单的讲解!
 
QMutex、QMutexLocker
QMutex类提供了一个保护一段临界区代码的方法,他每次只允许一个线程访问这段临界区代码。QMutex::lock()函数用来锁住互斥量,如果互斥量处于解锁状态,当前线程就会立即抓住并锁定它;否则当前线程就会被阻塞,直到持有这个互斥量的线程对其解锁。线程调用lock()函数后就会持有这个互斥量直到调用unlock()操作为止。QMutex还提供了一个tryLock()函数,如果互斥量已被锁定,就立即返回。
在问题出来了,如果要在stop()函数中使用mutex进行互斥操作,但unlock()操作写在那里?unlock()操作却不得不再return之后,从而导致unlock()操作永远也无法执行...
Qt提供了QMutexLocker类何以简化互斥量的处理,它在构造函数中接受一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量。
 
  bool Thread::stop()  
         {  
             QMutexLocker locker(&mutex);  
             m_stop = true;  
             return m_stop;  
        } 
QReadWriteLocker、QReadLocker、QWriteLocker
下面是一段对QReadWriteLocker类的对象进行,读写锁的操作,比较简单,这里也不多做讲解了,自己看吧
MyData data;
QReadWriteLock lock;
void ReaderThread::run()
{
...
lock.lockForRead();
access_data_without_modifying_it(&data);
lock.unlock();
...
}
void WriterThread::run()
{
...
lock.lockForWrite();
modify_data(&data);
lock.unlock();
...
}

QSemphore

Qt中的信号量是由QSemaphore类提供的,信号量可以理解为互斥量功能的扩展,互斥量只能锁定一次而信号量可以获取多次,它可以用来保护一定数量的同种资源。
acquire(n)函数用于获取n个资源,当没有足够的资源时调用者将被阻塞直到有足够的可用资源。release(n)函数用于释放n个资源。
QSemaphore类还提供了一个tryAcquire(n)函数,在没有足够的资源是该函数会立即返回。
一个典型的信号量应用程序是在两个线程间传递一定数量的数据(DataSize),而这两个线程使用一定大小(BufferSize)的共享循环缓存。
 
      const int DataSize = 100000;  
       const int BufferSize = 4096;  
       char buffer[BufferSize]; 
生产者线程向缓存中写入数据,直到它到达终点,然后在起点重新开始,覆盖已经存在的数据。消费者线程读取前者产生的数据。
生产者、消费者实例中对同步的需求有两处,如果生产者过快的产生数据,将会覆盖消费者还没有读取的数据,如果消费者过快的读取数据,将越过生产者并且读取到一些垃圾数据。
解决这个问题的一个有效的方法是使用两个信号量:
QSemaphore freeSpace(BufferSize);  
QSemaphore usedSpace(0); 
freeSpace信号量控制生产者可以填充数据的缓存部分。usedSpace信号量控制消费者可以读取的区域。这两个信号量是互补的。其中freeSpace信号量被初始化为BufferSize(4096),表示程序一开始有BufferSize个缓冲区单元可被填充,而信号量usedSpace被初始化为0,表示程序一开始缓冲区中没有数据可供读取。
对于这个实例,每个字节就看作一个资源,实际应用中常会在更大的单位上进行操作,从而减小使用信号量带来的开销。
void Producer::run()
{
for (int i = 0; i < DataSize; ++i) {
freeSpace.acquire();
buffer[i % BufferSize] = "MING"[uint(rand()) % 4];
usedSpace.release();
}
}

在生产者中,我们从获取一个“自由的”字节开始。如果缓存被消费者还没有读取的数据填满,acquire()的调用就会阻塞,直到消费者已经开始消耗这些数据为止。一旦我们已经获取了这个字节,我们就用一些随机数据("M"、"I"、"N"或"G")填充它并且把这个字节释放为“使用的”,所以它可以被消费者线程使用。

void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedSpace.acquire();
cerr << buffer[i % BufferSize];
freeSpace.release();
}
cerr << endl;
}
   int main()
{
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
QWaitCondition
对生产者和消费者问题的另一个解决方法是使用QWaitCondition,它允许线程在一定条件下唤醒其他线程。其中wakeOne()函数在条件满足时随机唤醒一个等待线程,而wakeAll()函数则在条件满足时唤醒所有等待线程
下面重写生产者和消费者实例,以QMutex为等待条件,QWaitCondition允许一个线程在一定条件下唤醒其他线程
   const int DataSize = 100000;  
   const int BufferSize = 4096;  
   char buffer[BufferSize];  
   QWaitCondition bufferIsNotFull;  
   QWaitCondition bufferIsNotEmpty;  
   QMutex mutex;  
   int usedSpace = 0;

在缓存之外,我们声明了两个QWaitCondition、一个QMutex和一个存储了在缓存中有多少个“使用的”字节的变量。
   void Producer::run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (usedSpace == BufferSize)
bufferIsNotFull.wait(&mutex);
buffer[i % BufferSize] = "MING"[uint(rand()) % 4];
++usedSpace;
bufferIsNotEmpty.wakeAll();
mutex.unlock();
}
}
在生产者中,我们从检查缓存是否充满开始。如果是充满的,我们等待“缓存不是充满的”条件。当这个条件满足时,我们向缓存写入一个字节,增加usedSpace,并且在唤醒任何等待这个“缓存不是空白的”条件变为真的线程
for循环中的所有语句需要使用互斥量加以保护,以保护其操作的原子性。
        bool wait ( QMutex * mutex, unsigned long time = ULONG_MAX ); 
这个函数做下说明,该函数将互斥量解锁并在此等待,它有两个参数,第一个参数为一个锁定的互斥量,第二个参数为等待时间。如果作为第一个参数的互斥量在调用是不是锁定的或出现递归锁定的情况,wait()函数将立即返回。
调用wait()操作的线程使得作为参数的互斥量在调用前变为锁定状态,然后自身被阻塞变成为等待状态直到满足以下条件:
其他线程调用了wakeOne()或者wakeAll()函数,这种情况下将返回"true"值。
第二个参数time超时(以毫秒记时),该参数默认情况是ULONG_MAX,表示永不超时,这种情况下将返回"false"值。
wait()函数返回前会将互斥量参数重新设置为锁定状态,从而保证从锁定状态到等待状态的原则性转换。
void Consumer::run()
{
forever {
mutex.lock();
if (usedSpace == 0)
bufferIsNotEmpty.wait(&mutex);
cerr << buffer[i % BufferSize];
--usedSpace;
bufferIsNotFull.wakeAll();
mutex.unlock();
}
cerr << endl;
}
消费者做的和生产者正好相反,他等待“缓存不是空白的”条件并唤醒任何等待“缓存不是充满的”的条件的线程
main()函数与上面的基本相同,这个不再多说。
在QThread类的静态函数currentThread(),可以返回当前线程线程ID。在X11环境下,这个ID是一个unsigned long类型的值。
自己总结:
原子类型  QAtomicInt QAtomicPointer 
 
2.多线程通信
  a . 全局变量 即共享内存
  b.  管道
  c.  信号槽
3.多进程通信
  信号槽  socket 文件映射

Qt 多线程同步与通信的更多相关文章

  1. QT多线程同步之QWaitcondition

    使用到多线程,无可避免的会遇到同步问题,qt提供几种同步线程的方法,在这里讲一下QWaitcondition的简单使用. 一.QWaitcondition,是通过一个线程达到某种条件来唤起另一个线程来 ...

  2. Qt多线程同步总结

    1.QMutex   QMutex mutex;   void func() { mutex.lock(); ........ mutex.unlock(); }   2.QMutex联手QMutex ...

  3. C#多线程的同步与通信

    C#中使用lock和Monitor控制多线程对资源的使用,最常见的生产者和消费者问题就是多线程同步和通信的经典例子.了解C#多线程的同步与通信. 一.关于lock和Monitor lock可以把一段代 ...

  4. C# 多线程同步和线程通信

    多线程通信 1. 当线程之间有先后的依赖关系时,属于线程之间的通信问题.也就是后一个线程要等待别的一个或多个线程全部完成,才能开始下一步的工作.可以使用: WaitHandle Class WaitH ...

  5. Win32多线程编程(3) — 线程同步与通信

      一.线程间数据通信 系统从进程的地址空间中分配内存给线程栈使用.新线程与创建它的线程在相同的进程上下文中运行.因此,新线程可以访问进程内核对象的所有句柄.进程中的所有内存以及同一个进程中其他所有线 ...

  6. Python 多线程、多进程 (二)之 多线程、同步、通信

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...

  7. Java精选笔记_多线程(创建、生命周期及状态转换、调度、同步、通信)

    线程概述 在应用程序中,不同的程序块是可以同时运行的,这种多个程序块同时运行的现象被称作并发执行. 多线程可以使程序在同一时间内完成很多操作. 多线程就是指一个应用程序中有多条并发执行的线索,每条线索 ...

  8. Qt QThread 线程创建,线程同步,线程通信 实例

    1.  继承QThread, 实现run()方法, 即可创建线程. 2. 实例1 代码 myThread.h #ifndef MYTHREAD_H #define MYTHREAD_H #includ ...

  9. java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)

    本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...

随机推荐

  1. 【MySQL函数】replace()函数

    1.replace()函数 语法:replace(a,b,c) a的b部分被c替换 案例:select replace(abcde,bcd,123)-- 结果a123e

  2. JavaScript学习笔记(一)——JS速览

    第一章 JS速览 1 限制时间处理事件 <script> setTomeout(wakeUpUser,5000); function wakeUpUser() { alert(" ...

  3. LeetCode-124.二叉树中的最大路径和

    给定一个非空二叉树,返回其最大路径和. 本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列.该路径至少包含一个节点,且不一定经过根节点. 示例 1: 输入: [1,2,3] 1 / \ 2 ...

  4. day-19 多种优化模型下的简单神经网络tensorflow示例

    如下样例基于tensorflow实现了一个简单的3层深度学习入门框架程序,程序主要有如下特性: 1.  基于著名的MNIST手写数字集样例数据:http://yann.lecun.com/exdb/m ...

  5. 算法笔记(c++)--完全背包问题

    算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1  价值为1,2,3,4,5  背包容量为10 # ...

  6. mysql group by 取第一条

    select * from table where id in (select max(id) from table group by sku) 说明:id是自增序列,sku是表中的一个字段

  7. 用 Python 编写的 Python 解释器

    Allison是Dropbox的工程师,在那里她维护着世界上最大的由Python客户组成的网络.在Dropbox之前,她是Recurse Center的引导师, … 她在北美的PyCon做过关于Pyt ...

  8. 20181120-6 Beta阶段第2周/共2周 Scrum立会报告+燃尽图 03

    此作业要求参见:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2411] 版本控制地址    [https://git.coding.n ...

  9. “Hello World!“”团队第五周召开的第二次会议

    今天是我们团队“Hello World!”团队第五周召开的第二次会议.也祝大家双十一快乐~~博客内容: 一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.todo list 六.会议照片 七 ...

  10. YQCB冲刺周第二天

    YQCB冲刺周第二天 1.实现用户记账的功能 2.实现用户头像的设置 3.实现个人设置的功能 遇到的问题: 记账的分类,数据库存取图片,页面跳转+超链接的使用 团队讨论的照片:             ...