一、同步线程方法

使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程。例如,如果两个线程试图同时写入相同的变量,结果是不确定的,所以需要同步线程。同步线程是一种保护共享资源等数据的常见的技术。迫使线程等待另一个的原则被称为互斥 。

Qt 中的 QMutex、QReadWriteLock、QSemaphore 和 QWaitCondition 类提供了同步线程的方法。

  • QMutex提供了一个互斥锁(mutex),在任何时间至多有一个线程可以获得mu­tex。 如果一个线程尝试获得 mutex,而此时 mutex 已经被锁住了 ,这个线程将会睡眠, 直到现在获得mutex的线程对mutex进行解锁为止。互斥锁经常用于对共享数据(例如,可以同时被多个线程访问的数据)的访问进行保护。
  • QReadWriteLock即读-写锁,与QMutex很相似,只不过它将对共享数据的访问区分为“读”访问和“写”访问,允许多个线程同时对数据进行“读”访问。在可能的情况下使用QReadWriteLock代替QMutex,可以提高多线程程序的并发度。
  • QSemaphore即信号量,是QMutex的一般化,用来保护一定数量的相同的资源,而互斥锁mutex只能保护一个资源。Qt之线程同步(生产者消费者模式 - QSemaphore) 提供了一个典型的案例,信号量:在“生产者 - 消费者”之间同步访问循环缓冲区。
  • QWaitCondition即条件变量,允许一个线程在一些条件满足时唤醒其他的线程。一个或者多个线程可以被阻塞来等待一个QWaitCondition来设置一个用于wakeOne()或者wakeAll()的条件。使用wakeOneO可以唤醒一个随机选取的等待线程,而使 用wakeAll()可以唤醒所有正在等待的线程。Qt之线程同步(生产者消费者模式 - QWaitCondition) 显示了如何使用 QWaitCondition 代替 QSemaphore 来实现“生产者 - 消费者”模式。

二、可重入与线程安全

这里的术语“可重入性”和“线程安全”被用来标记类与函数,以表明它们如何被应用在多线程应用程序中。

  • 一个线程安全的函数可以同时被多个线程调用,甚至调用者会使用共享数据也没有问题,因为对共享数据的访问是串行的。
  • 一个可重入函数也可以同时被多个线程调用,但是每个调用者只能使用自己的数据。

因此,一个线程安全的函数总是可重入的,但一个可重入的函数并不一定是线程安全的。扩展开来,一个可重入的类,指的是它的成员函数可以被多个线程安全地调用,只要每个线程使用这个类的不同的对象。而一个线程安全的类,指的是它的成员函数能够被多线程安全地调用,即使所有的线程都使用该类的同一个实例也没有关系。

注意: Qt的一些类被设计为线程安全的,如果它们的目的是多线程。如果一个函数没有被标记为线程安全的或可重入的,它就不应该被不同的线程使用。如果一个类没有被标记为线程安全的或可重入的,该类的实例就不应该被多个线程访问。

可重入性

C++的类往往是可重入的,这只是因为它们只能访问自己的数据。任何线程都能访问一个可重入类实例的一个成员函数,只要同一时间没有其它线程调用该实例的成员函数。例如,下面的Counter类就是可重入的:

class Counter
{
public:
Counter() { n = 0; } void increment() { ++n; }
void decrement() { --n; }
int value() const { return n; } private:
int n;
};

该类不是线程安全的,因为如果多个线程试图修改数据成员n,则结果是不确定的。这是因为++和–操作都不总是原子性的。事实上,它们一般被展开为3条机器指令:

  1. 将变量值装入寄存器
  2. 增加或减少寄存器中的值
  3. 将寄存器中的值写回内存

如果线程A和线程B同时将变量的旧值装入寄存器,增加寄存器中的值,再写回内存,它们最终会互相覆盖,导致变量值仅增加了一次!

线程安全

显然,访问应该是串行的: 线程A必须在无中断的情况下执行完1.2.3.三个步骤(原子性),然后线程B才能开始执行,反之亦然。一个使类是线程安全的简单方法就是用一个QMutex来保护数据成员的所有访问。

class Counter
{
public:
Counter() { n = 0; } void increment() { QMutexLocker locker(&mutex); ++n; }
void decrement() { QMutexLocker locker(&mutex); --n; }
int value() const { QMutexLocker locker(&mutex); return n; } private:
mutable QMutex mutex;
int n;
};

QMutexLocker类在其构造函数中自动锁定mutex,并且当析构函数被调用时解锁。锁定mutex保证了其它线程的访问都将是串行化的。mutex数据成员被声明为mutable的,这是因为value()是一个const函数,我们需要在其中lock和unlock该mutex。

Qt类的注意事项

许多Qt的类都是可重入的,但不是线程安全的,因为线程安全意味着为锁定与解锁一个QMutex增加额外的开销。例如:QString是可重入的,但不是线程安全的。你能够同时从多个线程访问不同的QString的实例,但不能同时从多个线程访问QString的同一个实例(除非用QMutex保护访问)。

有些Qt的类和函数是线程安全的。它们主要是线程相关类(例如:QMutex)和一些基本函数(例如: QCoreApplication::postEvent())。

参考:

Qt 之线程同步

Qt 之可重入与线程安全

Qt 进程和线程之三:线程同步、可重入与线程安全的更多相关文章

  1. Qt之可重入与线程安全

    简述 本篇文章中,术语"可重入性"和"线程安全"被用来标记类与函数,以表明它们如何被应用在多线程应用程序中. 一个线程安全的函数可以同时被多个线程调用,甚至调用 ...

  2. 可重入与线程安全(大多数Qt类是可重入,非线程安全的)

    可重入与线程安全 在Qt文档中,术语“可重入”与“线程安全”被用来说明一个函数如何用于多线程程序.假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是“可重入”的. ...

  3. 【C/C++】对于可重入、线程安全、异步信号安全几个概念的理解

    由于前段时间,程序偶尔异常挂起不工作,检查后发现时死锁了,原因就是:在信号处理函数里面调用了fprintf. printf等io函数是需要对输出缓冲区加锁,这类函数对本身是线程安全的,但是对信号处理函 ...

  4. Writing Reentrant and Thread-Safe Code(译:编写可重入和线程安全的代码)

    Writing Reentrant and Thread-Safe Code 编写可重入和线程安全的代码 (http://www.ualberta.ca/dept/chemeng/AIX-43/sha ...

  5. 可重入与线程安全(Reentrancy and Thread-Safety)

    http://blog.csdn.net/zzwdkxx/article/details/49338067 http://blog.csdn.net/zzwdkxx/article/details/4 ...

  6. PYTHON线程知识再研习D---可重入锁

    不多解释,预防普通锁不正规的获取与释放 #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time class ...

  7. linux可重入、异步信号安全和线程安全

    一 可重入函数 当一个被捕获的信号被一个进程处理时,进程执行的普通的指令序列会被一个信号处理器暂时地中断.它首先执行该信号处理程序中的指令.如果从信号处理程序返回(例如没有调用exit或longjmp ...

  8. Linux可重入函数和线程安全的区别与联系(转)

    *****可重入函数 函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入. 当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而 ...

  9. 【Linux】可重入函数和线程安全的区别与联系【转】

    转自:http://blog.csdn.net/scenlyf/article/details/52074444 版权声明:本文为博主原创文章,未经博主允许不得转载. *****可重入函数 函数被不同 ...

随机推荐

  1. React Native 隐藏组件思路

    In your render function:{ this.state.showTheThing && <TextInput/>} Then just do: this. ...

  2. centos 配置

    安装 node 源地址: http://my.oschina.net/blogshi/blog/260953 (一) 编译好的文件 简单说就是解压后,在bin文件夹中已经存在node以及npm,如果你 ...

  3. 字符编码乱码问题(servlet底层 编码大揭秘)

    好多初学者会遇到,请求过去的信息内包含中文(一般会是get方式提交过去的请求会出现).好郁闷,这是为什么呢.有下面分析下,说的不好可以吐槽 话说我们能遇到这种编码的问题,归根结底就是这  这 web开 ...

  4. [转载]Dalvik指令集

    这篇文章是转载的,为了便于查找一些指令,贴在这里. 转自:http://blog.csdn.net/canfengxiliu/article/details/20144119 ------------ ...

  5. 「网络流24题」「LuoguP3358」 最长k可重区间集问题(费用流

    题目描述 对于给定的开区间集合 I 和正整数 k,计算开区间集合 I 的最长 k可重区间集的长度. 输入输出格式 输入格式: 的第 1 行有 2 个正整数 n和 k,分别表示开区间的个数和开区间的可重 ...

  6. codevs 3095 黑心的市长

    3095 黑心的市长  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 钻石 Diamond   题目描述 Description A市有一条长Nkm的高速公路.有M个人各自想承包 ...

  7. Android实例1:button点击响应

    个人网站http://www.ravedonut.com/ Layout xml文件 <RelativeLayout android:layout_width="wrap_conten ...

  8. 第一篇:构建第一个SpringBoot工程

    简介 spring boot 它的设计目的就是为例简化开发,开启了各种自动装配,你不想写各种配置文件,引入相关的依赖就能迅速搭建起一个web工程.它采用的是建立生产就绪的应用程序观点,优先于配置的惯例 ...

  9. Eclipse如何查看接口实现类快捷键

    1.找到要打开的接口类 2.双击接口名选中 3.按Ctrl+T打开接口实现类 以List接口为例,如下所示

  10. mysql 三存储引擎

    一 什么是存储引擎 mysql中建立的库===>文件夹 库中建立的表===>文件 现实生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处理文本用txt类型 ...