线程互斥与同步:
1、互斥关系:线程间因相互竞争使用独占型资源所产生的制约关系。如争夺IO设备。
2、同步关系:为完成同一任务的伙伴线程间,因为需要在某些位置上协调它们的工作而相互等待、相互交换信息所产生的制约关系。
”++“操作符并不是原子操作,通常编译后它将被展开成如下三条机器指令:
1、将变量值载入寄存器。
2、将寄存器的值加1.
3、将寄存器中的值写回主存。
QT为实现线程的互斥与同步提供了以下几个常用类,它们是:QMutex,QMutexLocker,QReadWriteLocker,QReadLocker,QWriteLocker,QSemaphore,QWaitConditon。
QMutex类提供一个保护一段临界区代码的方法,它每次只允许一个线程访问这段临界代码。QMutex类的lock()函数用来锁住互斥量,如果互斥量处于解锁状态,当前线程就会立即抓住并锁定它,否则当前线程就会被阻塞,直到持有这个互斥量的线程对它解锁。
QMutexLocker类可以简化互斥量的处理,它在构造函数中接受一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量。代码如下:
class Key
{
public:
Key(){ key = 0;}
int createKey() { QMutexLocker locker(&mutex);++key; return key; }
int value() const
{ QMutexLocker locker(&mutex); return key; }
private:
int key;
QMutex mutex;
};
QSemaphore:互斥量只能锁定一次而信号量可以获取多次,信号量可以理解为对互斥量功能的扩展。它可以用来保护一定数量的同种资源。acquire(n);函数用于获取n个资源,当没有足够的资源时调用者将被阻塞直到有足够的可用资源。release(n)函数用于释放n个资源。QSemaphore类还提供了一个tryAcquire(n)函数,在没有足够的资源时该函数会立即返回。该信号量一般用于生产者/消费者的线程环境。
对于生产者/消费者的另一个解决方法就是QWaitCondition。它允许线程在一定条件下唤醒其他线程。
boo QWaitCondition::wait( QMutex* mutex, unsigned long time=ULONG_MAX)
该函数将互斥量解锁并在此等待,它带有两个参娄,第一个参数为一个锁定的互斥量,第二个参数为等待时间。如果作为第一个参数的互斥量在调用时不是锁定的或出现递归锁定的情况,wait函数将立刻返回。调用wait操作的线程使得作为参数的互斥量在调用前变为解锁定状态,然后自身被阻塞变为等待状态直到满足以下条件之一:
1、其他线程调用了wakeOne()或wakeAll()函数,这种情况下将返回true值。
2、第二个参数time超时,以毫秒记,该参数默认情况下为ULONG_MAX,表示永不超时,这种情况下将返回false值。
wait函数返回前会将互斥量参数重设为锁定状态,从而保证从锁定状态到等待状态的原子性转换。
QReadWriteLocker允许多个线程同时以只读方式访问,而一旦有写入操作时,所有线程将阻塞直到写入完成。
QT还提供了两类,它们分别是QReadLocker和QWriteLocker。这两个类在构造函数中接受一个读写锁(QReadWriteLocker),从而可以很方便地在构造时对其进行读锁定或写锁定,然后在析构时自动解除锁定。这一点与QMutexLocker和QMutex的关系是一样的。
产生线程死锁的4个必要条件:
1、互斥条件;
2、请求保持条件;
3、不可剥夺条件;
4、环路等待条件;
线程在访问全局变量时都使用了各自不同的互斥方法。但是某些多线程应用程序却需要在不同的线程中保存全局变量不同的值。这种变量通常被称为线程本地存储或线程特定数据。
QT提供QThreadStorage类用于存储线程的单独数据。这个类是一个模板类,由于编译器的限制,QThreadStorage类目前只能存储指针。其中setLocalData()函数为调用线程存储一份独立的数据,这个数据随后可以由localData函数访问。QThreadStorage类持有存储数据所有者线程的信息(存储的数据必须以new操作符在堆空间里分配),当所有者线程正常或非正常退出时,所存储的数据将自动释放。hasLocalData函数允许程序员判断线程中是否有经setLocalData函数存储的数据。
可重入:如果一个函数能同时被多个线程调用,并且为每一个调用者提供一份单独数据,那么称这个函数是可重入的。
线程安全:如果一个函数能同时被多个线程调用,并且所有的调用者引用同一份数据,访问数据时串行处理,那么称这个函数是线程安全的。
QT支持3种信号和槽的连接方式:
1、直接连接方式:信号发出后相应的槽函数将立即被调用。这个槽函数在发出信号的线程中执行,不一定必须在接收对象所属的线程中。
2、排队连接方式:信号发出后需等到接收对象所属线程的事件循环取得控制权时才调用响应的槽函数。这个槽函数在信号接收对象所属的同一个线程中执行。
3、自动连接方式:QT默认的方式,如果信号的发出与接收这个信号的对象同属一个线程,那么工作方式与直接连接方式相同,否则与排队连接方式相同。