Qt 进程和线程之三:线程同步、可重入与线程安全
一、同步线程方法
使用线程的目的是允许代码并行运行,但是有时线程必须停止并等待其他线程。例如,如果两个线程试图同时写入相同的变量,结果是不确定的,所以需要同步线程。同步线程是一种保护共享资源等数据的常见的技术。迫使线程等待另一个的原则被称为互斥 。
Qt 中的 QMutex、QReadWriteLock、QSemaphore 和 QWaitCondition 类提供了同步线程的方法。
- QMutex提供了一个互斥锁(mutex),在任何时间至多有一个线程可以获得mutex。 如果一个线程尝试获得 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条机器指令:
- 将变量值装入寄存器
- 增加或减少寄存器中的值
- 将寄存器中的值写回内存
如果线程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类是可重入,非线程安全的)
可重入与线程安全 在Qt文档中,术语“可重入”与“线程安全”被用来说明一个函数如何用于多线程程序.假如一个类的任何函数在此类的多个不同的实例上,可以被多个线程同时调用,那么这个类被称为是“可重入”的. ...
- 【C/C++】对于可重入、线程安全、异步信号安全几个概念的理解
由于前段时间,程序偶尔异常挂起不工作,检查后发现时死锁了,原因就是:在信号处理函数里面调用了fprintf. printf等io函数是需要对输出缓冲区加锁,这类函数对本身是线程安全的,但是对信号处理函 ...
- Writing Reentrant and Thread-Safe Code(译:编写可重入和线程安全的代码)
Writing Reentrant and Thread-Safe Code 编写可重入和线程安全的代码 (http://www.ualberta.ca/dept/chemeng/AIX-43/sha ...
- 可重入与线程安全(Reentrancy and Thread-Safety)
http://blog.csdn.net/zzwdkxx/article/details/49338067 http://blog.csdn.net/zzwdkxx/article/details/4 ...
- PYTHON线程知识再研习D---可重入锁
不多解释,预防普通锁不正规的获取与释放 #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time class ...
- linux可重入、异步信号安全和线程安全
一 可重入函数 当一个被捕获的信号被一个进程处理时,进程执行的普通的指令序列会被一个信号处理器暂时地中断.它首先执行该信号处理程序中的指令.如果从信号处理程序返回(例如没有调用exit或longjmp ...
- Linux可重入函数和线程安全的区别与联系(转)
*****可重入函数 函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入. 当程序运行到某一个函数的时候,可能因为硬件中断或者异常而使得在用户正在执行的代码暂时终端转而 ...
- 【Linux】可重入函数和线程安全的区别与联系【转】
转自:http://blog.csdn.net/scenlyf/article/details/52074444 版权声明:本文为博主原创文章,未经博主允许不得转载. *****可重入函数 函数被不同 ...
随机推荐
- Golang的一些学习
在 Go 中使用命名返回变量捕获 panic 在下面代码中,如果pressButton发生panic,那么不会执行到return err,导致返回的err是nil. func doStuff() er ...
- Double.valueOf()与Double.parseDouble()两者的区别
写代码用到这两个方法,不知道有什么区别,看一下源码: Double.parseDouble(String str) public static double parseDouble(String s) ...
- vue路由的两种模式,hash与history
对于Vue 这类渐进式前端开发框架,为了构建SPA(单页面应用),需要引入前端路由系统,这也就是Vue-router存在的意义.前端路由的核心,就在于——— 改变视图的同时不会向后端发出请求. 一.为 ...
- HDU4549 M斐波那契数列 —— 斐波那契、费马小定理、矩阵快速幂
题目链接:https://vjudge.net/problem/HDU-4549 M斐波那契数列 Time Limit: 3000/1000 MS (Java/Others) Memory Li ...
- BZOJ 3732 Network —— 最小生成树 + 倍增LCA
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3732 Description 给你N个点的无向图 (1 <= N <= 15, ...
- 步入C编程的第一天
我想学ruby以后开发网站,但ruby是高级语言,隐藏了许多底层的东西,因此先熟悉c语言 首先c程序的文件名是以.c结尾的 c程序的格式: 第一行#include<stdio.h> #是一 ...
- runtime之方法的交换
工作中没怎么用到runtime的东西,所以一直没怎么看,现在开始拿起来. runtime之方法的交换: 都知道OC中有category可以对已知类进行扩展,但是假如工程中需要修改某类的原方法,若用ca ...
- (转)定制findbugs规则
转载自http://www.51testing.com/html/97/13997-211893.html 这类文章极少,字节码操作需要对becl库及jvm字节码操作有一定常识.参考: http:// ...
- CodeForces768B:Code For 1 (分治)
Jon fought bravely to rescue the wildlings who were attacked by the white-walkers at Hardhome. On hi ...
- POJ2752(next原理理解)
Seek the Name, Seek the Fame Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 15536 Ac ...