操作系统中多线程的同步和互斥和Qt多线程

多个线程共享同一个进程的地址空间。所以线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制

线程同步

线程的同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。线程的同步是指在一些确定点上需要线程之间相互合作,协同工作。在访问同一个临界资源(互斥资源)时,两个线程间必须有一个先后顺序,因为临界资源一次只能供一个线程使用,如果两个线程都想要访问,这会形成死锁。

举例:假如程序中有一个静态变量,static int a;线程1负责往里写入数据,线程2需要读取其中的数据,那么线程2在读数据之前必须是线程1写入了数据,如果不是,那么线程2必须停下来等待线程1的操作结束。这就是线程之间在某些地方上的合作关系。

线程互斥

是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。线程的互斥是指同一进程中的多个线程之间因争用临界资源而互斥的执行,即一次只能有一个线程访问临界资源。 

举例:还是假如程序中有一个静态变量,static int b;线程1想要往里写入数据,线程2也想要往里写入数据,那么此时静态变量b就是一个临界资源(互斥资源),即一次只能被一个线程访问。如果线程1和线程2同时往b中写入数据,那怎么能行,计算机是不允许的!所以,要么是线程1占用b,此时线程2要等待;要么是线程2占用b,此时线程1等待。这里可以通过加锁的方式来实现。

线程间的同步方法大体可分为两类:用户模式内核模式

内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态。内核模式下的方法有:事件信号量互斥量

用户模式就是不需要切换到内核态,只在用户态完成操作。用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区

临界资源和临界区

临界资源(Critical Resource,CR)。临界资源一次仅允许被一个线程使用,也可以叫做互斥资源,即只能被各个线程互斥访问。它可以是一块内存、一个数据结构、一个文件或者任何其他具有排他性使用的东西。在程序中,通常竞争使用临界资源。这些必须互斥执行的代码段称为“临界区”(Critical Section,CS)。线程中对临界资源实施操作的那段程序,称之为临界区。临界区(代码段)实施对临界资源的操作,为了阻止问题的产生,一次只能有一个线程进入临界区。

四种进程或线程同步互斥的控制方法


1、临界区(Critical Section)

同一个进程内,实现互斥,通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。


2、互斥量(Mutex)

可以跨进程,实现互斥。为协调共同对一个共享资源的单独访问而设计的。 互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。


3、信号量(Semaphores)

主要是实现同步,可以跨进程。为控制一个具有有限数量用户资源而设计。 信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。


4、事 件(Event)

实现同步,可以跨进程。用来通知线程有一些事件已发生,从而启动后继任务的开始。 事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。 

Qt中的多线程控制

实现线程的互斥与同步常使用的类有QMutex、QMutexLocker、QReadWriteLocker、QReadLocker、QWriteLocker、QSemaphore和QWaitCondition。

下面举一个例子加以说明:

class Key
{
public:
    Key() {key=0;}
    int creatKey() {++key; return key;}
    int value()const {return key;}
private:
    int key;    
};

在多线程环境下,这个类是不安全的,因为存在多个线程同时修改私有成员key,其结果是不可预知的。 虽然Key类产生主键的函数creatKey()只有一条语句执行修改成员变量key的值,但是C++的“++”操作符并不是原子操作,通常编译后,它将被展开成为以下三条机器命令:

  1. 将变量值载入寄存器。
  2. 将寄存器中的值加1。
  3. 将寄存器中的值写回主存。

假设当前的key值为0,如果线程1和线程2同时将0值载入寄存器,执行加1操作并将加1后的值写会主存,则结果两个线程的执行结果将互相覆盖,实际上仅进行了一次加1操作,此时的key值为1.

互斥量-QMutex类和QMutexLocker类(加锁)

QMutex类

QMutex类的lock()函数用于锁住互斥量。如果互斥量处于解锁状态,则当前线程就会立即抓住并锁定它,否则当前线程就会被阻塞,直到持有这个互斥量的线程对它解锁。线程调用lock()函数后就会持有这个互斥量,直到调用unlock()操作为止。 QMutex类还提供了一个tryLock()函数。如果互斥量已被锁定,则立即返回。

class Key
{
public:
    Key() {key=0;}
    int creatKey() { mutex.lock();  ++key;  return key;  mutex. unlock();}
    int value()const { mutex.lock();  return key;  mutex.unlock();}
private:
    int key;
    QMutex mutex;
};

QMutexLocker类

Qt提供的QMutexLocker类可以简化互斥量的处理,它在构造函数中接收一个QMutex对象作为参数并将其锁定,在析构函数中解锁这个互斥量,这样就解决了以上问题。 例如:

class Key
{
public:
    Key() {key=0;}
    int creatKey() { QmutexLocker locker(&mutex);  ++key;  return key; }
    int value()const { QmutexLocker locker(&mutex);  return key; }
private:
    int key;
    QMutex mutex;
};

locker()函数作为局部变量会在函数退出时结束其作用域,从而自动对互斥量mutex解锁。

信号量

信号量可以理解为对互斥量功能的扩展,互斥量只能锁定一次而信号量可以获取多次,它可以用来保护一定数量的同种资源

信号量的典型用例是控制生产者/消费者之间共享的环形缓冲区。 生产者/消费者实例中对同步的需求有两处:

  • (1)如果生产者过快地生产数据,将会覆盖消费者还没有读取的数据。
  • (2)如果消费者过快地读取数据,将越过生产者并且读取到一些过期数据。

针对以上问题,有两种解决方法:

  • (1)首先使生产者填满整个缓冲区,然后等待消费者读取整个缓冲区,这是一种比较笨拙的方法。
  • (2)使生产者和消费者线程同时分别操作缓冲区的不同部分,这是一种比较高效的方法。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值