NPTL 线程同步方式

NPTL提供了互斥体 pthread_mutex_t 类型进行线程同步,防止由于多线程并发对全局变量造成的不正确操作。使用 pthread_mutext_t 对数据进行保护已经可以实现基本的数据同步,NPTL又提供了pthread_cond_t 条件变量与pthread_mutext_t一起使用实现高效的线程同步保护数据。有了互斥变量pthread_mutext_t为什么还要引入条件变量pthread_cond_t呢? 原因就是防止CPU空转,一个线程获得互斥量之后,另外一个线程如果想获取该互斥量,就会不断的去查询这个互斥量是否已经空闲可以被自己占用,于是浪费了CPU周期。引入条件变量pthread_cond_t之后,如果条件不满足,线程进入睡眠状态,不会浪费CPU周期。
NPTL进行线程同步的一般结构如下:

thread 1:
    pthread_mutex_lock(&mutex);
    while (!condition)
        pthread_cond_wait(&cond, &mutex);
    /* 实际操作,修改condition为无效 */
    pthread_mutex_unlock(&mutex);

thread2:
    pthread_mutex_lock(&mutex);
    /* 实际操作,修改condition为有效 */
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

标准代码结构是像上面这样,针对上面的结构提几个问题?

  1. 为什么要将pthread_cond_wait 放在while(!condition)循环内呢,为什么要有while(!condition)的存在呢?

    2. pthread_cond_t 为什么要和 pthread_mutex_t 一起使用呢,使用pthread_cond_signal的线程不使用pthread_mutext 行不行? 

  在回答上面的问题之前先介绍一下最核心的pthread_cond_wait(&cond, &mutex)在不同情况下都会干些什么。

  1. 程序运行到pthread_cond_wait() 条件发生,代码继续向下执行。

  2. 程序运行到pthread_cond_wait() 条件未发生,函数调用首先会释放mutex(打开锁),并使当前线程进入睡眠状态。

  3. 睡眠在pthread_cond_wait()上的线程被signal唤醒,pthread_cond_wait()首先去获得锁(尝试重新获得该mutex直到获得)。

  pthread_cond_wait()的行为为下面的讨论做一个铺垫。现在来考虑回答上面的问题,我们可以从多线程乱序执行做为切入点,thread1 有可能比thread2 先执行,thread2 也有可能比thread1先执行。

  1.首先考虑,如果thread2先执行并且已经执行到 pthread_cond_signal() 但是thread1甚至都还没有运行,更别说进入到pthread_cond_wait()状态,这时候没有 while(!condition) 会怎么样?

    显然thread2已经发送了singal了,但是没有接收者,此时出现了丢信号的情况,即如果没有 while(!condition) 当thread1进入到pthread_cond_wait()的时候就会睡眠,唤醒信号丢失的情况发生,在这种情况下如果有 while(!condition) 的存在则不会执行pthread_cond_wait() 直接执行下面的代码。

    那么用 if(!condtion) 不是也可以解决上面的问题吗? 不错是可以解决上面的问题,但是会带来新的问题。考虑这种情况:如果signal同时唤醒了多个wait在该条件上的线程(pthread_cond_broadcast 或者出现传说中的“惊群”效应),那使用if(!condtion) 就是不行的。 这是因为,各个多个被唤醒的线程肯定会有一个会先进入被这个mutex保护的临界区(回忆上面介绍的pthread_cond_wait()函数在线程醒来之前会尝试去持有锁直到持有为止),Linux上规定是低优先级的线程先获得该mutex,然后进行了操作,并修改了condition变量,释放了mutex,此时另一个正在睡眠中但同时也在尝试获取该mutex的线程被唤醒,然后直接就向下执行,此时就会导致多线程同步失败。如果使用while(!condtion)再次进行检查则不会出现同步失败的问题。      

  2.第二个问题相对简单,如果cond不和mutex一起使用,那么任何可以访问cond的线程都可能唤醒睡眠在某个mutex上的线程,所以需要mutex对cond的保护,以确保有资格的线程才能对某个线程进行唤醒操作。

理解条件变量的关键还是需要理解pthread_cond_wait()都干了些什么!转载自:http://www.cnblogs.com/zhuyp1015/p/4370170.html
时间: 2024-08-05 11:12:03

NPTL 线程同步方式的相关文章

线程同步方式比较

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区. 内核模式下的方法有:事件,信号量,互斥量. 临界区 保证在某一时刻只有一个线程能访问数据的简便办法.在任意时刻只允许一个线程对共享资源进行访问.如果有多个线程试图同时访问临界区,那么 在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开.临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操 作共享资源的目的. 仅能在同一进程内使用 互斥量 Mutex 互斥量跟临界区很相似,只有拥有互

线程同步方式之互斥量Mutex

互斥量和临界区非常相似,只有拥有了互斥对象的线程才可以访问共享资源,而互斥对象只有一个,因此可以保证同一时刻有且仅有一个线程可以访问共享资源,达到线程同步的目的. 互斥量相对于临界区更为高级,可以对互斥量进行命名,支持跨进程的线程同步.互斥量是调用的Win32的API对互斥锁的操作,因此在同一操作系统下不同进程可以按照互斥锁的名称共享锁. 正因为如此,互斥锁的操作会更好资源,性能上相对于临界区也有降低,在使用时还要多斟酌.对于进程内的线程同步使用临界区性能会更佳. 在.Net中使用Mutex类来

linux线程同步(队列方式)

看了一些关于信号量的线程同步方式,今天用了一下. 我对于线程同步一直有疑问,在主线程和子线程处理时间不相同的时候,用这种信号量,如何保证同步. 假如主线程比较快,信号量连加了n个,但是子线程就不断减这个n,减到0.但是如果主线程太快太快,需要停一停,比如缓冲区快溢出了,主线程需要挂起. 由什么来唤醒主线程呢?子线程?不过这样的话,容易造成主线程死锁,或者主和子都卡死. 下面的程序,没有用到信号量同步,信号量只是负责开启子线程而已.主要是队列的实现而已.等我把上面的问题解决完会写上更新的程序. 队

Win32 - 线程同步

线程的同步可分用户模式的线程同步和内核对象的线程同步两大类. 用户模式中线程的同步方法主要有原子访问和临界区等方法.其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合. 内核对象的线程同步则主要由事件.等待定时器.信号量以及信号灯等内核对象构成.由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式. 在WIN32中,同步机制主要有以下几种: (1)事件(Event)

C# 多线程编程第二步——线程同步与线程安全

上一篇博客学习了如何简单的使用多线程.其实普通的多线程确实很简单,但是一个安全的高效的多线程却不那么简单.所以很多时候不正确的使用多线程反倒会影响程序的性能. 下面先看一个例子 : class Program { static int num = 1; static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); //开始计时 stopWatch.Start(); ThreadStart threadStart

线程同步 – lock和Monitor

在多线程代码中,多个线程可能会访问一些公共的资源(变量.方法逻辑等等),这些公共资源称为临界区(共享区):临界区的资源是不安全,所以需要通过线程同步对多个访问临界区的线程进行控制. 同样,有些时候我们需要多个线程按照特定的顺序执行,这时候,我们也需要进行线程同步. 下面,我们就看看C#中通过lock和Monitor进行线程同步. lock关键字 lock是一种非常简单而且经常使用的线程同步方式,lock 关键字将语句块标记为临界区. lock 确保当一个线程位于代码的临界区时,另一个线程不能进入

Delphi 线程同步技术(转)

上次跟大家分享了线程的标准代码,其实在线程的使用中最重要的是线程的同步问题,如果你在使用线程后,发现你的界面经常被卡死,或者无法显示出来,显示混乱,你的使用的变量值老是不按预想的变化,结果往往出乎意料,那么你很有可能是忽略了线程同步的问题. 当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源.例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数.当然,在把整个文件调入内存之前,统计它的计数是没有意义的.但是,由于每个操作都有自己的 线程,操作系统

mfc小工具开发之定时闹钟之---多线程急线程同步

一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等.用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等.但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务. 在MFC中,一般用全局函数Afx

临界区,互斥量,信号量,事件的区别(线程同步)

(转)临界区,互斥量,信号量,事件的区别(线程同步) (转)临界区,互斥量,信号量,事件的区别(线程同步) . 分类: C++ windows 核心编程 2012-04-10 14:55 3321人阅读 评论(0) 收藏 举报 semaphoremfcnulleventsthreadhttp服务器 四种进程或线程同步互斥的控制方法 1.临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问. 2.互斥量:为协调共同对一个共享资源的单独访问而设计的. 3.信号量:为控制一个