精华内容
下载资源
问答
  • 多线程(互斥锁、读写锁、条件变量、信号量、自旋锁)相关
    千次阅读
    2017-08-22 14:52:17

    概述

    同步和互斥在多线程和多进程编程中是一个基本的需求,互相协作的多个进程和线程往往需要某种方式的同步和互斥。POSIX定义了一系列同步对象用于同步和互斥。
    同步对象是内存中的变量属于进程中的资源,可以按照与访问数据完全相同的方式对其进行访问。默认情况下POSIX定义的这些同步对象具有进程可见性,即同步对象只对定义它的进程可见;但是通过修改同步对象的属性可以使得同步对象对不同的进程可见,具体的做法是:

    • 修改同步对象的属性为PTHREAD_PROCESS_SHARED
    • 在进程的特殊内存区域--共享内存中创建同步对象
    这样创建的同步对象将对共享该共享内存的所有进程可见,这些进程可以使用该同步对象进行同步互斥。
    其中设置共享对象的属性为 PTHREAD_PROCESS_SHARED是为了告诉系统该共享对象是跨越进程的 ,不仅仅对创建它的进程可见;但是仅有这一个条件显然无法满足不同进程使用该同步对象的需求,因为每个进程的地址空间是独立的, 位于一个进程的普通内存区域中的对象是无法被其它进程所访问的,能满足这一要求的内存区域是共享内存,因而 同步对象要在进程的共享内存区域内创建
    同步对象还可以放在文件中。同步对象可以比创建它的进程具有更长的生命周期。


    最常见的进程/线程的同步方法有互斥锁(或称互斥量Mutex),读写锁(rdlock),条件变量(cond),信号量(Semophore)等。在Windows系统中,临界区(Critical Section)和事件对象(Event)也是常用的同步方法。

     

    1.互斥锁--保护了一个临界区,在这个临界区中,一次最多只能进入一个线程。如果有多个进程在同一个临界区内活动,就有可能产生竞态条件(race condition)导致错误,其中包含递归锁和非递归锁,(递归锁:同一个线程可以多次获得该锁,别的线程必须等该线程释放所有次数的锁才可以获得)。

     

    2.读写锁--从广义的逻辑上讲,也可以认为是一种共享版的互斥锁。可以多个线程同时进行读,但是写操作必须单独进行,不可多写和边读边写。如果对一个临界区大部分是读操作而只有少量的写操作,读写锁在一定程度上能够降低线程互斥产生的代价。

     

    3.条件变量--允许线程以一种无竞争的方式等待某个条件的发生。当该条件没有发生时,线程会一直处于休眠状态。当被其它线程通知条件已经发生时,线程才会被唤醒从而继续向下执行。条件变量是比较底层的同步原语,直接使用的情况不多,往往用于实现高层之间的线程同步。使用条件变量的一个经典的例子就是线程池(Thread Pool)了。

     

    4.信号量--通过精心设计信号量的PV操作,可以实现很复杂的进程同步情况(例如经典的哲学家就餐问题和理发店问题)。而现实的程序设计中,却极少有人使用信号量。能用信号量解决的问题似乎总能用其它更清晰更简洁的设计手段去代替信号量。 


    5.自旋锁--当要获取一把自旋锁的时候又被别的线程持有时,不断循环的去检索是否可以获得自旋锁,一直占CPU资源。 

     
    对于这些同步对象,有一些共同点:

    1. 每种类型的同步对象都有一个init的API,它完成该对象的初始化,在初始化过程中会分配该同步对象所需要的资源(注意是为支持这种锁而需要的资源,不包括表示同步对象的变量本身所需要的内存)
    2. 每种类型的同步对象都一个destory的API,它完成与init相反的工作
    3. 对于使用动态分配内存的同步对象,在使用它之前必须先调用init
    4. 在释放使用动态分配内存的同步对象所使用的内存时,必须先调用destory释放系统为其申请的资源
    5. 每种同步对象的默认作用范围都是进程内部的线程,但是可以通过修改其属性为PTHREAD_PROCESS_SHARED并在进程共享内存中创建它的方式使其作用范围跨越进程范围
    6. 无论是作用于进程内的线程,还是作用于不同进程间的线程,真正参与竞争的都是线程(对于不存在多个线程的进程来说就是其主线程),因而讨论都基于线程来
    7. 这些同步对象都是协作性质的,相当于一种君子协定,需要相关线程主动去使用,无法强制一个线程必须使用某个同步对象

    总体上来说,可以将它们分为两类:

    1. 第一类是互斥锁、读写锁、自旋锁,它们主要是用来保护临界区的,也就是主要用于解决互斥问题的,当尝试上锁时大体上有两种情况下会返回:上锁成功或出错,它们不会因为出现信号而返回。另外解锁只能由锁的拥有着进行
    2. 第二类是条件变量和信号量,它们提供了异步通知的能力,因而可以用于同步和互斥。但是二者又有区别:
      • 信号量可以由发起P操作的线程发起V操作,也可以由其它线程发起V操作;但是条件变量一般要由其它线程发起signal(即唤醒)操作
      • 由于条件变量并没有包含任何需要检测的条件的信息,因而对这个条件需要用其它方式来保护,所以条件变量需要和互斥锁一起使用,而信号量本身就包含了相关的条件信息(一般是资源可用量),因而不需要和其它方式一起来使用
      • 类似于三种锁,信号量的P操作要么成功返回,要么失败返回,不会因而出现信号而返回;但是条件变量可能因为出现信号而返回,这也是因为它没包含相关的条件信息而导致的。

    文章会涉及到递归锁与非递归锁(recursive mutexnon-recursive mutex),区域锁(Scoped Lock),策略锁(Strategized Locking),读写锁与条件变量,双重检测锁(DCL),锁无关的数据结构(Locking free),自旋锁等等内容


    一、互斥锁

    互斥锁(mutex):顾名思义即是相互排斥的锁,它是最基本的同步形式,用于保护临界区,以保证任何时刻都只有一个线程在执行临界区中的代码。在任意时刻都只有一个线程能持有这把锁,当已经有线程拿到这把锁的时候,其它请求获得这把锁的线程将会被阻塞直到持有锁的线程释放了这把锁或者得到一个EBUSY的错误。

    最常见互斥场景是:多个线程会访问它们共享的资源,为了保证资源的一致性,因而需要进行互斥访问,以保证任何时刻都只有一个线程在访问该资源。因而大部分场景下临界区中放的都是操作互斥资源的代码。
    互斥锁解决同步问题就不是那么合适了,因为需要同步的线程之间往往有依赖或者顺序关系,但是互斥锁自己无法保证这个顺序

    临界区的基本形式为:

       mutex_lock(...);

    临界区
    mutex_unlock(...);
    即位于lock之后,unlock之前的部分是临界区。

    1、创建和销毁

    创建和销毁
    有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
     在LinuxThreads实现中,pthread_mutex_t是一个结构,而 PTHREAD_MUTEX_INITIALIZER则是一个结构常量
    动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下: 
    int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) 
    mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性,和静态方式等效, PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查
    pthread_mutex_destroy ()用于注销一个互斥锁,API定义如下: 
    int pthread_mutex_destroy(pthread_mutex_t *mutex) 
    销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
    默认的属性
    • pshared:          PTHREAD_PROCESS_PRIVATE
    • type:                  PTHREAD_MUTEX_DEFAULT
    • protocol:           PTHREAD_PRIO_NONE
    • prioceiling:       – 
    • robustness:    PTHREAD_MUTEX_STALLED_NP

        2、属性对象

    使用pthread_mutexattr_init(可以将与互斥锁对象相关联的属性初始化为其缺省值。在执行过程中,线程系统会为每个属性对象分配 存储空间
    pthread_mutexattr_init 语法
    int pthread_mutexattr_init(pthread_mutexattr_t *mattr);
    #include <pthread.h>
    pthread_mutexattr_t mattr;
    int ret;/* initialize an attribute to default value */
    ret = pthread_mutexattr_init(&mattr);
    调用此函数时,pshared 属性的 缺省值为 PTHREAD_PROCESS_PRIVATE。该值表示可以在进程内使用经过初始化的互斥锁。
    mattr 范围可能的值为 PTHREAD_PROCESS_PRIVATE 和 PTHREAD_PROCESS_SHARED (跨进程)。
    对于互斥锁属性对象,必须首先通过调用 pthread_mutexattr_destroy将其销毁所创建的属性对象的存储空间,才能重新初始化该对象。pthread_mutexattr_init()调用会导致分配类型为 opaque 的对象。如果未销毁该对象,则会导致内存泄漏。
    pthread_mutexattr_destroy 语法
    int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr)
    #include <pthread.h>
    pthread_mutexattr_t mattr ;
    int ret;/* destroy an attribute */ret = pthread_mutexattr_destroy(&mattr);
    pthread_mutexattr_destroy()成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。

    3、类型属性

    pthread_mutexattr_settype(3C)可用来设置互斥锁的 type属性。
    pthread_mutexattr_gettype(3C)可用来获取由 pthread_mutexattr_settype()设置的互斥锁的 type属性。
     语法
    #include <pthread.h>
    int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);
    int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。
    类型属性的 缺省值为 PTHREAD_MUTEX_DEFAULT。
    type参数指定互斥锁的类型。以下列出了有效的互斥锁类型:
    PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_TIMED_NP
    描述: 此类型的互斥锁不会检测死锁。 如果线程在不首先解除互斥锁的情况下尝试重新锁定该互斥锁,则会产生死锁 。尝试解除由其他线程锁定的互斥锁会产生不确定的行为。如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为, 当一个线程加锁以后,其余请求锁的线程将形成一个 等待队列 ,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性
    PTHREAD_MUTEX_ERRORCHECK ( PTHREAD_MUTEX_ERRORCHECK_NP)
    描述:  检错锁 ,如果同一个线程请求同一个锁,则返回EDEADLK,否则PTHREAD_MUTEX_TIMED_NP类型动作相同。 如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
    PTHREAD_MUTEX_RECURSIVE ( PTHREAD_MUTEX_RECURSIVE_NP)
    描述:  嵌套锁(递归锁,可重入锁) 与 PTHREAD_MUTEX_NORMAL 类型的互斥锁不同,对此类型互斥锁进行重新锁定时不会产生死锁情况。多次锁定互斥锁需要进行相同次数的解除锁定才可以释放该锁,然后其他线程才能获取该互斥锁。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。 如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
    PTHREAD_MUTEX_DEFAULT
    描述:如果尝试以 递归方式锁定此类型的互斥锁,则会产生不确定的行为。对于不是由调用线程锁定的此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。允许在实现中将该互斥锁映射到其他互斥锁类型之一。对于 Solaris 线程,PTHREAD_PROCESS_DEFAULT 会映射到 PTHREAD_PROCESS_NORMAL。
    * PTHREAD_MUTEX_ADAPTIVE_NP
    描述: 适应锁 当线程尝试申请锁,会自动根据拥有锁的线程繁忙或sleep来选择是busy wait还是sleep。这种锁只有Solaris内核提供 仅等待解锁后重新竞争。
    pthread_mutexattr_settype 返回值
    如果运行成功,pthread_mutexattr_settype 函数会返回零。否则,将返回用于指明错误的错误号。

    4、锁的操作

    锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而 检错锁则必须由加锁者解锁才有效,否则返回EPERM ;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。
    int pthread_mutex_lock(pthread_mutex_t *mutex)
    int pthread_mutex_unlock(pthread_mutex_t *mutex)
    int pthread_mutex_trylock(pthread_mutex_t *mutex)
    pthread_mutex_trylock()语义与pthread_mutex_lock()类似,是非阻塞,不同的是在锁已经被占据时返回EBUSY而不是挂起等待,否则获得该锁。

    5、其他

    POSIX 线程锁机制的Linux实现都不是取消点,因此,延迟取消类型的线程不会因收到取消信号而离开加锁等待 。值得注意的是,如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,或者设置了异步取消类型,则必须在退出回调函数中解锁。
    这个锁机制同时也不是异步信号安全的,也就是说,不应该在 信号处理过程中使用互斥锁,否则容易造成死锁。
    互斥锁属性使用互斥锁(互斥)可以使线程按 顺序执行。通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程。互斥锁还可以保护 单线程代码。
    要更改缺省的互斥锁属性,可以对属性对象进行声明和初始化。通常,互斥锁属性会设置在应用程序开头的某个位置,以便可以快速查找和轻松修改。
     6、设置范围
    pthread_mutexattr_setpshared 可用来设置互斥锁 变量作用域
    pthread_mutexattr_getpshared可用来返回由 pthread_mutexattr_setpshared() 定义的互斥锁 变量 的范围
    语法
    int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared);
    int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int *pshared);
    #include <pthread.h>
    pthread_mutexattr_t mattr;
    int ret;
    ret = pthread_mutexattr_init(&mattr);/* * resetting to its default value: private */
    ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);
    ret = pthread_mutexattr_getpshared(&mattr, &pshared); 此函数可为属性对象 mattr 获取 pshared 的当前值。
    互斥锁 变量可以是进程专用的(进程内)变量,也可以是系统范围内的(进程间)变量。要在多个进程中的线程之间共享互斥锁,可以在 共享内存中创建互斥锁,并将 pshared属性设置为 PTHREAD_PROCESS_SHARED。 此行为与最初的 Solaris 线程实现中 mutex_init()中的 USYNC_PROCESS 标志等效。
    如果互斥锁的 pshared属性设置为 PTHREAD_PROCESS_PRIVATE,则仅有那些由同一个进程创建的线程才能够处理该互斥锁。
     7、设置协议
    pthread_mutexattr_setprotocol 可用来设置互斥锁属性对象的协议属性。
    pthread_mutexattr_getprotocol 可用来获取互斥锁属性对象的协议属性。
    语法
    #include <pthread.h>
    int pthread_mutexattr_setprotocol(pthread_mutexattr_t *mattr, int protocol);
    int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);
    protocol 可定义应用于互斥锁属性对象的协议。
    pthread.h 中定义的 protocol 可以是以下值之一:PTHREAD_PRIO_NONE、PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT。
    PTHREAD_PRIO_NONE
    线程的优先级和调度不会受到互斥锁拥有权的影响。
    PTHREAD_PRIO_INHERIT
    此协议值(如 thrd1) 会影响线程的优先级和调度 。如果更高优先级的线程因 thrd1 所拥有的一个或多个互斥锁而被阻塞,而这些互斥锁是用 PTHREAD_PRIO_INHERIT 初始化的,则 thrd1 将以高于它的优先级或者所有正在等待这些互斥锁(这些互斥锁是 thrd1 指所拥有的互斥锁)的线程的最高优先级运行。
    如果 thrd1 因另一个线程 (thrd3) 拥有的互斥锁而被阻塞,则相同的优先级继承效应会以递归方式传播给 thrd3。
    使用 PTHREAD_PRIO_INHERIT 可以 避免优先级倒置 如果没有优先级继承,底优先级的线程可能会在很长一段时间内都得不到调度,而这会导致等待低优先级线程锁持有的锁的高优先级线程也等待很长时间(因为低优先级线程无法运行,因而就无法释放锁,所以高优先级线程只能继续阻塞在锁上)使用优先级继承可以短时间的提高低优先级线程的优先级,从而使它可以尽快得到调度,然后释放锁。低优先级线程在释放锁后就会恢复自己的优先级。

    当前线程已经拥有互斥锁,如果定义了 _POSIX_THREAD_PRIO_INHERIT 符号,则会使用协议属性值 PTHREAD_PRIO_INHERIT 对互斥锁进行初始化。属主失败时的行为取决于pthread_mutexattr_setrobust_np()的 robustness 参数的值。

    解除锁定互斥锁。
    互斥锁的下一个属主将获取该互斥锁,并返回错误 EOWNERDEAD。
    互斥锁的下一个属主会尝试使该互斥锁所保护的状态一致。如果上一个属主失败,则状态可能会不一致。如果属主成功使状态保持一致,则可针对该互斥锁调用 pthread_mutex_init()并解除锁定该互斥锁。
    注 –如果针对以前初始化的但尚未销毁的互斥锁调用pthread_mutex_init(),则该互斥锁不会重新初始化。
    如果属主无法使状态保持一致, 请勿调用 pthread_mutex_init(),而是解除锁定该互斥锁。在这种情况下,所有等待的线程都将被唤醒。以后对 pthread_mutex_lock()的所有调用将无法获取互斥锁,并将返回错误代码 ENOTRECOVERABLE。现在,通过调用 pthread_mutex_destroy()来取消初始化该互斥锁,即可使其状态保持一致。调用 pthread_mutex_init()可重新初始化互斥锁。
    如果已获取该锁的线程失败并返回 EOWNERDEAD,则下一个属主将获取该锁及错误代码 EOWNERDEAD。
    PTHREAD_PRIO_PROTECT
    此协议值 会影响其他线程(如 thrd2)的优先级和调度 。thrd2 以其较高的优先级或者以 thrd2 拥有的所有互斥锁的最高优先级上限运行。基于被 thrd2 拥有的任一互斥锁阻塞的较高优先级线程对于 thrd2 的调度没有任何影响。
    如果某个线程调用sched_setparam()来更改初始优先级,则调度程序不会采用新优先级将该线程移到调度队列末尾。
    一个 线程可以同时拥有多个混合使用 PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 初始化的互斥锁。在这种情况下,该线程将以通过其中任一协议获取的最高优先级执行。
    PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 只有在采用实时调度策略SCHED_FIFO 或 SCHED_RR的优先级进程内可用。
    pthread_mutexattr_setprotocol 返回值
    如果成功完成, pthread_mutexattr_setprotocol()会返回 0。其他任何返回值都表示出现了错误。
    如果出现以下任一情况, pthread_mutexattr_setprotocol()将失败并返回对应的值。
    8、设置上限
    pthread_mutexattr_setprioceiling  可用来设置互斥锁属性对象的优先级上限属性。
    pthread_mutexattr_getprioceiling  可用来获取互斥锁属性对象的优先级上限属性。
    pthread_mutexattr_setprioceiling 语法
    #include <pthread.h>
    int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *mattr, int prioceiling, int *oldceiling);
    int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *mattr, int *prioceiling);
    prioceiling 指定已初始化互斥锁的优先级上限。 优先级上限定义执行互斥锁保护的临界段时的最低优先级 。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。 可锁定互斥锁(如果未锁定的话),或者一直处于 阻塞状态 ,直到 pthread_mutex_setprioceiling() 成功锁定该互斥锁,更改该互斥锁的优先级上限并将该互斥锁释放为止。锁定互斥锁的过程无需遵循优先级保护协议。
    如果 pthread_mutex_setprioceiling()成功,则将在 old_ceiling 中返回以前的优先级上限值。如果 pthread_mutex_setprioceiling()失败,则互斥锁的优先级上限保持不变。
    oldceiling 包含以前的优先级上限值。
    注 –仅当定义了 _POSIX_THREAD_PRIO_PROTECT 符号时,attr 互斥锁属性对象才会包括优先级上限属性。
    pthread_mutexattr_setprioceiling 返回值
    如果成功完成, pthread_mutexattr_setprioceiling()会返回 0。其他任何返回值都表示出现了错误。
    9、强健属性
    robustness 定义在互斥锁的持有者“死亡”时的行为
    np后缀的,表示not portable,就是不可移值的意思,这些函数是一些系统自己实现的,而不是POSIX标准。
    pthread_mutexattr_setrobust_np  可用来设置互斥锁属性对象的强健属性。
    pthread_mutexattr_setrobust_np 语法
    #include <pthread.h>
    int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *mattr, int *robustness);
    注 –仅当定义了符号 _POSIX_THREAD_PRIO_INHERIT 时,pthread_mutexattr_setrobust_np()才适用。
    robustness 定义在互斥锁的属主失败时的行为。pthread.h 中定义的 robustness 的值为 PTHREAD_MUTEX_ROBUST_NP 或 PTHREAD_MUTEX_STALLED_NP。缺省值为 PTHREAD_MUTEX_STALLED_NP。
    PTHREAD_MUTEX_ROBUST_NP
    如果互斥锁的持有者死亡,则以后对 pthread_mutex_lock() 的所有调用将以不确定的方式被阻塞。
    PTHREAD_MUTEX_STALLED_NP
    如果互斥锁的持有者“死亡”了,或者持有这样的互斥锁的进程unmap了互斥锁所在的共享内存或者持有这样的互斥锁的进程执行了exec调用,则会解除锁定该互斥锁。互斥锁的下一个持有者将获取该互斥锁,并返回错误 EOWNWERDEAD。
    注 – 如果互斥锁具有PTHREAD_MUTEX_ROBUST_NP的属性,则应用程序在获取该锁时必须检查 pthread_mutex_lock 的返回代码看获取锁时是否返回了EOWNWERDEAD错误。
    互斥锁的新属主应使该互斥锁所保护的状态保持一致。如果上一个属主失败,则互斥锁状态可能会不一致。
    如果新属主能够使状态保持一致,请针对该互斥锁调用pthread_mutex_consistent_np(),并解除锁定该互斥锁。
    如果新属主无法使状态保持一致,请勿针对该互斥锁调用pthread_mutex_consistent_np(),而是解除锁定该互斥锁。
    所有等待的线程都将被唤醒,以后对pthread_mutex_lock()的所有调用都将无法获取该互斥锁。 返回代码为 ENOTRECOVERABLE,就意味这这个锁不能被使用了。通过调用pthread_mutex_destroy()取消对互斥锁的初始化,并调用pthread_mutex_int()重新初始化该互斥锁,可使该互斥锁保持一致。
    如果已获取该锁的线程失败并返回 EOWNERDEAD,则下一个属主获取该锁时将返回代码 EOWNERDEAD。
    pthread_mutexattr_setrobust_np 返回值
    如果成功完成, pthread_mutexattr_setrobust_np()会返回 0。其他任何返回值都表示出现了错误。
    如果出现以下任一情况, pthread_mutexattr_setrobust_np()将失败并返回对应的值。

     10、互斥锁的相关实现与效率问题

    互斥锁实际的效率还是可以让人接受的,加锁的时间大概100ns左右,而实际上互斥锁的一种可能的实现是先自旋一段时间,当自旋的时间超过阀值之后再将线程投入睡眠中,因此在并发运算中使用互斥锁(每次占用锁的时间很短)的效果可能不亚于使用自旋锁。

    二、读写锁
    读写锁实际是一种特殊的 自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于 自旋锁而言,能提高 并发性,因为在 多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
    在读写锁保持期间也是抢占失效的。
    如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。
    1、特性
    一次只有一个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁.  正是因为这个特性,
    当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞.
    当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的 线程都可以得到访问权, 但是如果线程希望以写模式对此锁进行加锁, 它必须直到知道所有的线程释放锁.
    通常,  当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁长期占用, 而等待的写模式锁请求长期阻塞.
    2、适用性
    读写锁适合于对数据结构的读次数比写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁又叫共享-独占锁.
    3、初始化和销毁
    #include <pthread.h>
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    成功则返回0, 出错则返回错误编号.
    互斥量以上, 在释放读写锁占用的内存之前, 需要先通过pthread_rwlock_destroy对读写锁进行清理工作, 释放由init分配的资源.
    4、读和写
    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    成功则返回0, 出错则返回错误编号.
    这3个函数分别实现获取读锁, 获取写锁和释放锁的操作. 获取锁的两个函数是阻塞操作, 同样, 非阻塞的函数为:
    #include <pthread.h>
    int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    成功则返回0, 出错则返回错误编号.
    非阻塞的获取锁操作, 如果可以获取则返回0, 否则返回错误的EBUSY.

    三、条件变量


    1、特性
    不同于互斥锁,互斥锁主要用于上锁,而条件变量用于等待。它特别适合需要进行同步的问题,比如线程A,B存在依赖关系,B要在某个条件发生之后才能继续执行,而这个条件只有A才能满足,这个时候就可以使用条件变量来完成这个事情:
    • 创建和该条件相关联的条件变量,并初始化它
    • 对于线程A来说,它需要做的是设置这个条件,通知等待在相关联条件变量上的线程
    • 对于线程B来说,它需要做的是检查这个条件,如果不满足自己的要求,就阻塞在相关联的条件变量上
    由于条件变量并没有包含任何需要检测的条件的信息,因而对这个条件需要用其它方式来保护,所以条件变量需要和互斥锁一起使用,每个条件变量总是有一个互斥锁和其关联。如果线程未持有与条件相关联的互斥锁,则调用pthread_cond_signal或pthread_cond_broadcast会产生唤醒丢失错误,满足以下所有条件时,即会出现唤醒丢失问题:
    1.  一个线程调用 pthread_cond_signal或 pthread_cond_broadcast
    2.  另一个线程已经测试了该条件,但是尚未调用 pthread_cond_wait
    3.  没有正在等待的线程,因而pthread_cond_signal或 pthread_cond_broadcast的唤醒将无法起作用,该唤醒会被丢失

    对比下信号,信号可以做到通知其它线程某件事发生了,接收信号的线程只需要注册一个信号处理函数,然后信号发生后该处理函数就会被系统调用,一旦该函数被调用了就意味着注册时关联的信号所代表的事情发生了。但要注意:

    1. POSXI要求多线程应用中信号处理程序必须在应用的多个线程之间共享(即在一个进程的多个线程之间共享),因而对于同一个进程中的多个线程来说它们必须共享信号处理程序,信号处理程序无法确定信号是被发给谁的
    2. 使用信号时只需要注册信号处理程序即可,不需要创建某种同步对象,而使用条件变量需要创建同步对象,如果要在进程间进行同步和互斥还对条件变量的作用域和属性有要求
    3. 有权限的任何用户的任何程序都可以发送信号给一个线程,而使用条件变量时,相关的线程必须可以访问同步对
     举例                                                                                     
        设有两个共享的变量 x 和 y,通过互斥量 mut 保护,当 x > y 时,条件变量 cond 被触发。先执行func1,之后再执行func2,
                 int x = 0,y = 5;
                 pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
                 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
                 void *func1(void *arg)
         { 
           pthread_mutex_lock(&mut);
                 while (x <= y) {
                         pthread_cond_wait(&cond, &mut);
                  }
                 /* 对 x、y 进行操作 */
                  pthread_mutex_unlock(&mut);
                 }                                                                             
            void *func2(void *arg)
         {                                                                                          
                 pthread_mutex_lock(&mut);
                 x = 5;
    y = 0;
                 if (x > y) 
    pthread_cond_broadcast(&cond);
                 pthread_mutex_unlock(&mut);
         }
       2、初始化

    如果条件变量变量是静态的则可以直接用PTHREAD_COND_INITIALIZER来初始化它,
    比如:static pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER
    如果条件变量是动态分配的,则必须在使用它之前用pthread_cond_init来初始化它。pthread_cond_init用来初始化cv所指向的条件变量,如果cattr为NULL则会用缺省的属性初始化条件变量;否则使用cattr指定的属性初始化条件变量。使用PTHREAD_COND_INITIALIZER 宏与动态分配具有null 属性的 pthread_cond_init()等效,不同之处在于PTHREAD_COND_INITIALIZER 宏不进行错误检查。
    多个线程决不能同时初始化或重新初始化同一个条件变量。如果要重新初始化或销毁某个条件变量,则应用程序必须确保该条件变量未被使用。

    3、条件变量的阻塞

    pthread_cond_wait以原子方式释放mutex所指向的互斥锁,并导致调用线程阻塞在cv所指向的条件变量上。阻塞的线程可以通过如下方式被唤醒:
    • 由pthread_cond_signal唤醒
    • 由pthread_cond_broadcast唤醒
    • 由信号唤醒
    pthread_cond_wait返回时,由mutex指定的互斥锁被锁定并且被调用线程锁持有,即使返回错误时也是如此。
    pthread_cond_wait在被唤醒之前将一致保持阻塞状态。它会在被阻塞之前以原子方式释放相关的互斥锁,并在返回之前以原子方式再次获取该互斥锁
    通常情况下对条件表达式的检查是在互斥锁的保护下进行的。如果条件表达式为假,线程就会基于条件变量阻塞。然后,当其它线程更改条件值时,就会唤醒它(通过pthread_cond_signal或pthread_cond_broadcast)。 这种变化会导致至少一个正在等待该条件的线程解除阻塞并尝试再次获取互斥锁。
    必须重新测试导致等待的条件,然后才能从 pthread_cond_wait处继续执行
    。唤醒的线程重新获取互斥锁并从pthread_cond_wait返回之前,条件可能会发生变化。等待线程锁等待的条件可能并未真正发生。通常使用条件变量的方式如下:
    pthread_mutex_lock();
    while(condition_is_false)
    pthread_cond_wait();
    pthread_mutex_unlock();
    pthread_cond_wait是一个取消点。如果有一个未决的取消请求并且该线程启用了取消功能,则该线程会被终止并在继续持有锁的状态下开始执行的清理处理函数。如果清理处理函数中未释放锁,则就会出现线程终止但是未释放锁的情形。

    4、解除阻塞

    pthread_cond_signal解除阻塞在该条件变量上的一个线程的阻塞状态。
    应在互斥锁的保护下修改相关条件,该互斥锁应该是与该条件变量相关联的那个互斥锁(即调用wati时指定的那个互斥锁)。否则,可能在条件变量的测试和pthread_cond_wait阻塞之间修改该变量,这会导致无限期等待。
    如果有多个线程在等待一个条件变量,则线程被唤醒的顺序由所采用的调度策略决定。
    • 如果使用的是默认的调度策略,即SCHED_OTHER,则无法保证被唤醒的顺序
    • 如果使用的是SCHED_FIFO 或SCHED_RR,则线程按照优先级被唤醒
    如果没有任何线程基于条件变量阻塞,则调用 pthread_cond_signal不起作用。

    5、指定时间内解除阻塞

    pthread_cond_timedwait的用法与 pthread_cond_wait的用法基本相同,区别在于在由abstime指定的时间之后不再被阻塞。
    pthread_cond_reltimedwait_np与pthread_cond_timedwait基本相同,它们唯一的区别在于pthread_cond_reltimedwait_np使用相对时间间隔而不是将来的绝对时间作为其最后一个参数的值。
    类似于pthread_cond_wait,pthread_cond_reltimedwait_np和pthread_cond_timedwait也是取消点。

    6、解除所有阻塞
    pthread_cond_broadcast解除所有基于该条件变量阻塞的线程的阻塞。 应在互斥锁的保护下修改相关条件,该互斥锁应该是与该条件变量相关联的那个互斥锁(即调用wati时指定的那个互斥锁)。否则,可能在条件变量的测试和pthread_cond_wait阻塞之间修改该变量,这会导致无限期等待。
    由于pthread_cond_broadcast会导致所有基于该条件阻塞的线程再次争用互斥锁,因此即便使用了pthread_cond_broadcast实际上最终也只有一个线程可以获得锁并开始运行。虽然都是只有一个线程可以运行,但是这种情形与pthread_cond_signal是有所区别的:
    • 如果有多个线程阻塞在条件变量上,并且pthread_cond_signal唤醒了其中一个线程,则其它线程仍然在等待被唤醒然后再尝试获取相应的互斥锁,它们阻塞在条件变量上
    • 如果有多个线程阻塞在条件变量上,并且pthread_cond_broadcast唤醒它们,则所有线程都开始竞争互斥锁,胜利者开始执行,失败者阻塞在互斥锁上
    如果没有任何线程基于条件变量阻塞,则调用pthread_cond_broadcast不起作用。
    四、信号量
    由于信号量内容比较多.单独整合。
    五、自旋锁

    1、特性
    自旋锁是SMP架构中的一种low-level的同步机制。
    当线程A想要获取一把自选锁而该锁又被其它线程锁持有时,线程A会在一个循环中自选以检测锁是不是已经可用了。对于自选锁需要注意:
    • 由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,这就会浪费CPU时间。
    • 持有自旋锁的线程在sleep之前应该释放自旋锁以便其它线程可以获得自旋锁。(在内核编程中,如果持有自旋锁的代码sleep了就可能导致整个系统挂起,最近刚解决了一个内核中的问题就是由于持有自旋锁时sleep了,然后导致所有的核全部挂起(是一个8核的CPU))
    使用任何锁需要消耗系统资源(内存资源和CPU时间),这种资源消耗可以分为两类:
    1. 建立锁所需要的资源
    2. 当线程被阻塞时锁所需要的资源
    对于自旋锁来说,它只需要消耗很少的资源来建立锁;随后当线程被阻塞时,它就会一直重复检查看锁是否可用了,也就是说当自旋锁处于等待状态时它会一直消耗CPU时间。
    对于 互斥锁 来说,与自旋锁相比它需要 消耗大量的系统资源来建立锁 ;随后当线程被阻塞时,线程的调度状态被修改,并且线程被加入等待线程队列;最后当锁可用时,在获取锁之前,线程会被从等待队列取出并更改其调度状态;但是在线程被阻塞期间,它不消耗CPU资源。

    因此自旋锁和互斥锁适用于不同的场景。自旋锁适用于那些仅需要阻塞很短时间的场景,而互斥锁适用于那些可能会阻塞很长时间的场景。


     2、创建和销毁

    int pthread_spin_init(pthread_spinlock_t *lock, int pshared);//创建锁
    int pthread_spin_destroy(pthread_spinlock_t *lock);//销毁锁
    pthread_spin_init用来申请使用自旋锁所需要的资源并且将它初始化为非锁定状态。pshared的取值及其含义:
    • PTHREAD_PROCESS_SHARED:该自旋锁可以在多个进程中的线程之间共享。
    • PTHREAD_PROCESS_PRIVATE:仅初始化本自旋锁的线程所在的进程内的线程才能够使用该自旋锁。
    pthread_spin_destroy用来销毁指定的自旋锁并释放所有相关联的资源(所谓的所有指的是由pthread_spin_init自动申请的资源)在调用该函数之后如果没有调用pthread_spin_init重新初始化自旋锁,则任何尝试使用该锁的调用的结果都是未定义的。如果调用该函数时自旋锁正在被使用或者自旋锁未被初始化则结果是未定义的。

    3、加锁和解锁

    int pthread_spin_lock(pthread_spinlock_t *lock);//阻塞加锁
    int pthread_spin_trylock(pthread_spinlock_t *lock); //非阻塞加锁
    int pthread_spin_unlock(pthread_spinlock_t *lock);//解锁
    更多相关内容
  • 一、线程锁(互斥锁)在一个程序内,主进程可以启动很线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身的调度规则,...

    一、线程锁(互斥锁)

    在一个程序内,主进程可以启动很多个线程,这些线程都可以访问主进程的内存空间,在Python中虽然有了GIL,同一时间只有一个线程在运行,可是这些线程的调度都归系统,操作系统有自身的调度规则,所以就可能造成,

    假设两个线程都在访问 global count= 0, 每个进程都会执行 count +=1 。(1)(2)(3)第一个线程申请GIL然后,读取global count到及进程到 cpu ,(4)然后cpu执行到一半,(5)把这个线程停了,将上下文保存到自身寄存器中。注意这时候没返回结果。这时候解释器就会把GIL释放,

    (6)(7)(8)第二个线程申请GIL执行如上步骤,读取count= 0 ,cpu计算结束后将结果count=1 返回,(10)(11)解释器拿到结果进行赋值,此时count=1,这个进程运行结束,(12)(13)然后cpu从寄存器中第一个线程的执行状态,继续执行,注意这个读取的是寄存器中的上下文,就是第一次执行时 count= 0,及第一个线程的上下文。计算结果返回count =1 返回。就造成了结果的错误。

    6d2e910cd0cdf271e8b1e376b0305df0.png

    1.1 python中的线程锁(互斥锁mutex)

    使用threading模块中的Lock类,得到一个全局锁实例。然后 Lock()实例下 有一个 acquire 方法为加锁, release 方法释放锁

    import threading

    count = 0

    lock = threading.Lock() #申请一个锁

    def count_():

    global count #声明为全局变量

    lock.acquire() #加锁,锁释放前不予许其他程序执行

    count += 1

    lock.release() #执行完 ,释放锁

    thread_list = []

    for i in range(10):

    t = threading.Thread(target= count_)

    t.start()

    thread_list.append(t)

    for t in thread_list:

    t.join() #主程序等待所有程序执行完毕

    print('Count:{: ^4}'.format(count))

    1.2 递归锁

    基本用不上,主要目的就是防止锁里面加锁,然后程序无法解锁退出。

    import threading,time

    def run1():

    print("grab the first part data")

    lock.acquire()

    global num

    num +=1

    lock.release()

    return num

    def run2():

    print("grab the second part data")

    lock.acquire()

    global num2

    num2+=1

    lock.release()

    return num2

    def run3():

    lock.acquire()

    res = run1()

    print('--------between run1 and run2-----')

    res2 = run2()

    lock.release()

    print(res,res2)

    if __name__ == '__main__':

    num,num2 = 0,0

    lock = threading.RLock()

    for i in range(10):

    t = threading.Thread(target=run3)

    t.start()

    while threading.active_count() != 1:

    print(threading.active_count())

    else:

    print('----all threads done---')

    print(num,num2)

    经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇:

    &lpar;转&rpar;经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇:

    多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

    秒杀多线程第七篇 经典线程同步 互斥量Mutex

    本文转载于:http://blog.csdn.net/morewindows/article/details/7470936 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用 ...

    转--- 秒杀多线程第七篇 经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇:

    python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)

    9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕​详细解释:1.主 ...

    JoinableQueue队列,线程,线程于进程的关系,使用线程,线程的特点,守护线程,线程的互斥锁,死锁问题,递归锁,信号量

    1.JoinableQueue队列 JoinableQueue([maxsize]):这就像是一个Queue对象,但是队列允许项目的使用者通知生成者项目已经被成功处理.通知进程是使用共享的信号和条件变 ...

    线程全局修改、死锁、递归锁、信号量、GIL以及多进程和多线程的比较

    线程全局修改 x = 100 def func1(): global x print(x) changex() print(x) def changex(): global x x = 50 func ...

    并发编程(五)——GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue

    GIL.死锁现象与递归锁.信号量.Event事件.线程queue 一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多 ...

    随机推荐

    php二维数组排序的处理

    一维数组排序可以使用asort.ksort等一些方法进程排序,相对来说比较简单.二维数组的排序怎么实现呢?使用array_multisort和usort可以实现 例如像下面的数组:    代码如下: ...

    在Handler&period;ashx文件中使用session

    使用jquery调用handler文件中的方法,需要使用session,默认生成的文件中,不可以直接使用session.按照以下步骤,即可以通过session与其他的aspx页面的session进行数 ...

    如何在search中动态的显示和隐藏tree中的字段

    在tree定义 invisible 来自context

    展开全文
  • 一、同步与互斥的概念   现代操作系统基本都是任务操作系统,即同时有大量可调度实体在运行。在任务操作系统中,同时运行的个任务可能: 都需要访问/使用同一种资源; 个任务之间有依赖关系,某个...

    一、同步与互斥的概念

      现代操作系统基本都是多任务操作系统,即同时有大量可调度实体在运行。在多任务操作系统中,同时运行的多个任务可能:

    • 都需要访问/使用同一种资源
    • 多个任务之间有依赖关系,某个任务的运行依赖于另一个任务。

    【同步】:

      是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。

    【互斥】:

      是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。

    二、互斥锁(同步)

      在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。
      在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )

    【互斥锁的特点】:

    1. 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

    2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

    3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

    【互斥锁的操作流程如下】:

    1. 在访问共享资源后临界区域前,对互斥锁进行加锁;

    2. 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;

    3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

    #include <pthread.h>
    #include <time.h>
    // 初始化一个互斥锁。
    int pthread_mutex_init(pthread_mutex_t *mutex, 
    						const pthread_mutexattr_t *attr);
    
    // 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,
    // 直到互斥锁解锁后再上锁。
    int pthread_mutex_lock(pthread_mutex_t *mutex);
    
    // 调用该函数时,若互斥锁未加锁,则上锁,返回 0;
    // 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
    int pthread_mutex_trylock(pthread_mutex_t *mutex);
    
    // 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量
    // 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。
    int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
    const struct timespec *restrict abs_timeout);
    
    // 对指定的互斥锁解锁。
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    // 销毁指定的一个互斥锁。互斥锁在使用完毕后,
    // 必须要对互斥锁进行销毁,以释放资源。
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    

    【Demo】(阻塞模式):

    //使用互斥量解决多线程抢占资源的问题
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <string.h>
     
    char* buf[5]; //字符指针数组  全局变量
    int pos; //用于指定上面数组的下标
     
    //1.定义互斥量
    pthread_mutex_t mutex;
     
    void *task(void *p)
    {
        //3.使用互斥量进行加锁
        pthread_mutex_lock(&mutex);
     
        buf[pos] = (char *)p;
        sleep(1);
        pos++;
     
        //4.使用互斥量进行解锁
        pthread_mutex_unlock(&mutex);
    }
     
    int main(void)
    {
        //2.初始化互斥量, 默认属性
        pthread_mutex_init(&mutex, NULL);
     
        //1.启动一个线程 向数组中存储内容
        pthread_t tid, tid2;
        pthread_create(&tid, NULL, task, (void *)"zhangfei");
        pthread_create(&tid2, NULL, task, (void *)"guanyu");
        //2.主线程进程等待,并且打印最终的结果
        pthread_join(tid, NULL);
        pthread_join(tid2, NULL);
     
        //5.销毁互斥量
        pthread_mutex_destroy(&mutex);
     
        int i = 0;
        printf("字符指针数组中的内容是:");
        for(i = 0; i < pos; ++i)
        {
            printf("%s ", buf[i]);
        }
        printf("\n");
        return 0;
    }
    

    【Demo】(非阻塞模式):

    #include <stdio.h>
    #include <pthread.h>
    #include <time.h>
    #include <string.h>
     
    int main (void)
    {
        int err;
        struct timespec tout;
        struct tm *tmp;
        char buf[64];
        pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
        
        pthread_mutex_lock (&lock);
        printf ("mutex is locked\n");
        clock_gettime (CLOCK_REALTIME, &tout);
        tmp = localtime (&tout.tv_sec); 
        strftime (buf, sizeof (buf), "%r", tmp);
        printf ("current time is %s\n", buf);
        tout.tv_sec += 10;
        err = pthread_mutex_timedlock (&lock, &tout);
        clock_gettime (CLOCK_REALTIME, &tout);
        tmp = localtime (&tout.tv_sec);
        strftime (buf, sizeof (buf), "%r", tmp);
        printf ("the time is now %s\n", buf);
        if (err == 0)
            printf ("mutex locked again\n");
        else 
            printf ("can`t lock mutex again:%s\n", strerror (err));
        return 0;
    }
    

    三、条件变量(同步)

      与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用
      条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:

    • 一个线程等待"条件变量的条件成立"而挂起;
    • 另一个线程使 “条件成立”(给出条件成立信号)。

    【原理】:

      条件的检测是在互斥锁的保护下进行的。线程在改变条件状态之前必须首先锁住互斥量。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量 可以被用来实现这两进程间的线程同步。

    【条件变量的操作流程如下】:

    1. 初始化:init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL;

    2. 等待条件成立:pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait);

    3. 激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)

    4. 清除条件变量:destroy;无线程等待,否则返回EBUSY清除条件变量:destroy;无线程等待,否则返回EBUSY

    #include <pthread.h>
    // 初始化条件变量
    int pthread_cond_init(pthread_cond_t *cond,
    						pthread_condattr_t *cond_attr);
    
    // 阻塞等待
    int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
    
    // 超时等待
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,
    						const timespec *abstime);
    
    // 解除所有线程的阻塞
    int pthread_cond_destroy(pthread_cond_t *cond);
    
    // 至少唤醒一个等待该条件的线程
    int pthread_cond_signal(pthread_cond_t *cond);
    
    // 唤醒等待该条件的所有线程
    int pthread_cond_broadcast(pthread_cond_t *cond);  
    

    1、线程的条件变量实例1

      Jack开着一辆出租车来到一个站点停车,看见没人就走了。过段时间,Susan来到站点准备乘车,但是没有来,于是就等着。过了一会Mike开着车来到了这个站点,Sunsan就上了Mike的车走了。如图所示:
    在这里插入图片描述

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <pthread.h>  
      
    pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER;  
    pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER;  
      
    void *traveler_arrive(void *name)  
    {  
        char *p = (char *)name;  
      
        printf ("Travelr: %s need a taxi now!\n", p);  
        // 加锁,把信号量加入队列,释放信号量
        pthread_mutex_lock(&taximutex);  
        pthread_cond_wait(&taxicond, &taximutex);  
        pthread_mutex_unlock(&taximutex);  
        printf ("traveler: %s now got a taxi!\n", p);  
        pthread_exit(NULL);  
    }  
      
    void *taxi_arrive(void *name)  
    {  
        char *p = (char *)name;  
    
        printf ("Taxi: %s arrives.\n", p);
        // 给线程或者条件发信号,一定要在改变条件状态后再给线程发信号
        pthread_cond_signal(&taxicond);  
        pthread_exit(NULL);  
    }  
      
    int main (int argc, char **argv)  
    {  
        char *name;  
        pthread_t thread;  
        pthread_attr_t threadattr; // 线程属性 
        pthread_attr_init(&threadattr);  // 线程属性初始化
      
        // 创建三个线程
        name = "Jack";  
        pthread_create(&thread, &threadattr, taxi_arrive, (void *)name);  
        sleep(1);  
        name = "Susan";  
        pthread_create(&thread, &threadattr, traveler_arrive, (void *)name);  
        sleep(1);  
        name = "Mike";  
        pthread_create(&thread, &threadattr, taxi_arrive, (void *)name);  
        sleep(1);  
      
        return 0;  
    }
    

    2、线程的条件变量实例2

      Jack开着一辆出租车来到一个站点停车,看见没人就等着。过段时间,Susan来到站点准备乘车看见了Jack的出租车,于是就上去了。过了一会Mike开着车来到了这个站点,看见没人救等着。如图所示:
    在这里插入图片描述

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
     
    int travelercount = 0;
    pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER;
     
    void *traveler_arrive(void *name)
    {
        char *p = (char *)name;
     
        pthread_mutex_lock(&taximutex);
     
        printf ("traveler: %s need a taxi now!\n", p);
        travelercount++;
        pthread_cond_wait(&taxicond, &taximutex);
                
        pthread_mutex_unlock(&taximutex);
        printf ("traveler: %s now got a taxi!\n", p);
        pthread_exit(NULL);
    }
     
    void *taxi_arrive(void *name)
    {
        char *p = (char *)name;
        printf ("Taxi: %s arrives.\n", p);
        for(;;)
        {
            if(travelercount)
            {
                pthread_cond_signal(&taxicond);
                travelercount--;
                break;
            }
        }
        pthread_exit(NULL);
    }
     
    int main (int argc, char **argv)
    {
        char *name;
        pthread_t thread;
        pthread_attr_t threadattr;
        pthread_attr_init(&threadattr);
     
        name = "Jack";
        pthread_create(&thread, &threadattr, taxi_arrive, name);
        sleep(1);
        name = "Susan";
        pthread_create(&thread, &threadattr, traveler_arrive, name);
        sleep(3);
        name = "Mike";
        pthread_create(&thread, &threadattr, taxi_arrive, name);
        sleep(4);
     
        return 0;
    }
    

    3、虚假唤醒(spurious wakeup)

      虚假唤醒(spurious wakeup)在采用条件等待时,我们使用的是:

    while(条件不满足)
    {  
       condition_wait(cond, mutex);  
    }  
    // 而不是:  
    If( 条件不满足 )
    {  
       Condition_wait(cond,mutex);  
    }   
    

    这是因为可能会存在虚假唤醒”spurious wakeup”的情况。

      也就是说,即使没有线程调用condition_signal, 原先调用condition_wait的函数也可能会返回此时线程被唤醒了,但是条件并不满足,这个时候如果不对条件进行检查而往下执行,就可能会导致后续的处理出现错误。

      虚假唤醒在linux的多处理器系统中/在程序接收到信号时可能回发生。在Windows系统和JAVA虚拟机上也存在。在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。

    四、读写锁(同步)

      读写锁与互斥量类似,不过读写锁允许更改的并行性,也叫共享互斥锁。互斥量要么是锁住状态,要么就是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态

      一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁(允许多个线程读但只允许一个线程写)

    【读写锁的特点】:

    • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作;

    • 如果有其它线程写数据,则其它线程都不允许读、写操作

    【读写锁的规则】:

    • 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁;

    • 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。

    读写锁适合于对数据结构的读次数比写次数多得多的情况。

    #include <pthread.h>
    // 初始化读写锁
    int pthread_rwlock_init(pthread_rwlock_t *rwlock, 
    						const pthread_rwlockattr_t *attr); 
    
    // 申请读锁
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock ); 
    
    // 申请写锁
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); 
    
    // 尝试以非阻塞的方式来在读写锁上获取写锁,
    // 如果有任何的读者或写者持有该锁,则立即失败返回。
    int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 
    
    // 解锁
    int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); 
    
    // 销毁读写锁
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    

    【Demo】:

    // 一个使用读写锁来实现 4 个线程读写一段数据是实例。
    // 在此示例程序中,共创建了 4 个线程,
    // 其中两个线程用来写入数据,两个线程用来读取数据
    #include <stdio.h>  
    #include <unistd.h>  
    #include <pthread.h>  
    
    pthread_rwlock_t rwlock; //读写锁  
    int num = 1;  
      
    //读操作,其他线程允许读操作,却不允许写操作  
    void *fun1(void *arg)  
    {  
        while(1)  
        {  
            pthread_rwlock_rdlock(&rwlock);
            printf("read num first == %d\n", num);
            pthread_rwlock_unlock(&rwlock);
            sleep(1);
        }
    }
      
    //读操作,其他线程允许读操作,却不允许写操作  
    void *fun2(void *arg)
    {
        while(1)
        {
            pthread_rwlock_rdlock(&rwlock);
            printf("read num second == %d\n", num);
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
        }
    }
     
    //写操作,其它线程都不允许读或写操作  
    void *fun3(void *arg)
    {
        while(1)
        {
            pthread_rwlock_wrlock(&rwlock);
            num++;
            printf("write thread first\n");
            pthread_rwlock_unlock(&rwlock);
            sleep(2);
        }
    }
     
    //写操作,其它线程都不允许读或写操作  
    void *fun4(void *arg)
    {
        while(1)
        {  
            pthread_rwlock_wrlock(&rwlock);  
            num++;  
            printf("write thread second\n");  
            pthread_rwlock_unlock(&rwlock);  
            sleep(1);  
        }  
    }  
      
    int main()  
    {  
        pthread_t ptd1, ptd2, ptd3, ptd4;  
          
        pthread_rwlock_init(&rwlock, NULL);//初始化一个读写锁  
          
        //创建线程  
        pthread_create(&ptd1, NULL, fun1, NULL);  
        pthread_create(&ptd2, NULL, fun2, NULL);  
        pthread_create(&ptd3, NULL, fun3, NULL);  
        pthread_create(&ptd4, NULL, fun4, NULL);  
          
        //等待线程结束,回收其资源  
        pthread_join(ptd1, NULL);  
        pthread_join(ptd2, NULL);  
        pthread_join(ptd3, NULL);  
        pthread_join(ptd4, NULL);  
          
        pthread_rwlock_destroy(&rwlock);//销毁读写锁  
          
        return 0;  
    }  
    

    五、自旋锁(同步)

      自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。
      自旋锁在用户态使用的比较少,在内核使用的比较多!自旋锁的使用场景:锁的持有时间比较短,或者说小于2次上下文切换的时间
      自旋锁在用户态的函数接口和互斥量一样,把pthread_mutex_xxx()中mutex换成spin,如:pthread_spin_init()。

    六、信号量(同步与互斥)

      信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。

      编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。PV 原语是对信号量的操作,一次 P 操作使信号量减1,一次 V 操作使信号量加1。

    #include <semaphore.h>
    // 初始化信号量
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    
    // 信号量 P 操作(减 1)
    int sem_wait(sem_t *sem);
    
    // 以非阻塞的方式来对信号量进行减 1 操作
    int sem_trywait(sem_t *sem);
    
    // 信号量 V 操作(加 1)
    int sem_post(sem_t *sem);
    
    // 获取信号量的值
    int sem_getvalue(sem_t *sem, int *sval);
    
    // 销毁信号量
    int sem_destroy(sem_t *sem);
    

    【信号量用于同步】:
    在这里插入图片描述

    // 信号量用于同步实例
    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
     
    sem_t sem_g,sem_p;   //定义两个信号量
    char ch = 'a';
     
    void *pthread_g(void *arg)  //此线程改变字符ch的值
    {
        while(1)
        {
            sem_wait(&sem_g);
            ch++;
            sleep(1);
            sem_post(&sem_p);
        }
    }
     
    void *pthread_p(void *arg)  //此线程打印ch的值
    {
        while(1)
        {
            sem_wait(&sem_p);
            printf("%c",ch);
            fflush(stdout);
            sem_post(&sem_g);
        }
    }
     
    int main(int argc, char *argv[])
    {
        pthread_t tid1,tid2;
        sem_init(&sem_g, 0, 0); // 初始化信号量为0
        sem_init(&sem_p, 0, 1); // 初始化信号量为1
        
        // 创建两个线程
        pthread_create(&tid1, NULL, pthread_g, NULL);
        pthread_create(&tid2, NULL, pthread_p, NULL);
        
        // 回收线程
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL);
        
        return 0;
    }
    

    【信号量用于互斥】:
    在这里插入图片描述

    // 信号量用于互斥实例
    #include <stdio.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <semaphore.h>
     
    sem_t sem; //信号量
     
    void printer(char *str)
    {
        sem_wait(&sem);//减一,p操作
        while(*str) // 输出字符串(如果不用互斥,此处可能会被其他线程入侵)
        {
            putchar(*str);  
            fflush(stdout);
            str++;
            sleep(1);
        }
        printf("\n");
        
        sem_post(&sem);//加一,v操作
    }
     
    void *thread_fun1(void *arg)
    {
        char *str1 = "hello";
        printer(str1);
    }
     
    void *thread_fun2(void *arg)
    {
        char *str2 = "world";
        printer(str2);
    }
     
    int main(void)
    {
        pthread_t tid1, tid2;
        
        sem_init(&sem, 0, 1); //初始化信号量,初始值为 1
        
        //创建 2 个线程
        pthread_create(&tid1, NULL, thread_fun1, NULL);
        pthread_create(&tid2, NULL, thread_fun2, NULL);
        
        //等待线程结束,回收其资源
        pthread_join(tid1, NULL);
        pthread_join(tid2, NULL); 
        
        sem_destroy(&sem); //销毁信号量
        
        return 0;
    }
    

    参考:https://blog.csdn.net/lianghe_work/article/details/47747413
    https://blog.csdn.net/lianghe_work/article/details/47747497
    https://blog.csdn.net/fulinus/article/details/39342371
    https://blog.csdn.net/lianghe_work/article/details/47775637
    https://www.cnblogs.com/wuchanming/p/4411908.html
    https://blog.csdn.net/qq_29350001/article/details/71709326
    https://blog.csdn.net/lianghe_work/article/details/47775741

    展开全文
  • 信号量是在并行处理环境中对个处理器访问某个公共资源进行保护的机制,Muter用于互斥操作。 mutex的语义相对于信号量要简单轻便一些,在争用激烈的测试场景下,Mutex比信号量执行速度更快,可拓展性更好,另外...

    1、互斥体概述

    互斥概念:只能有一个人使用


    信号量是在并行处理环境中对多个处理器访问某个公共资源进行保护的机制,Muter用于互斥操作。


    mutex的语义相对于信号量要简单轻便一些,在锁争用激烈的测试场景下,Mutex比信号量执行速度更快,可拓展性更好,另外Mutex数据结构的定义比信号量小

    2、Mutex(互斥体)的使用注意事项

    ·同一时刻只有一个线程可以持有Mutex
    ·只有锁持有着可以解锁,不能在一个进程中持有Mutex,在另外一个进程中释放它
    ·不允许递归的加锁和解锁
    ·当进程持有Mutex时,进程不可以退出
    ·Mutex必须使用官方的API初始化
    ·Mutex可以睡眠,所以不允许在中断处理程序或者中断下半部中使用,例如tasklet、定时器等

    3、初始化

    静态定义:
    DEFINE_MUTEX(name);
    
    动态初始化mutex:
    mutex_init(&mutex);
    

    4、互斥锁的操作

    mutex_lock(struct mutex*);
    		·为指定的mutex上锁,如果不可用则睡眠
    		
    mutex_unlock(struct mutex*);
    		·为指定的mutex解锁
    		
    mutex_trylock(struct mutex*);
    		·尝试获取指定的mutex,如果成功则返回1;否则锁被获取,返回值是0
    		
    mutex_is_lock(struct mutex*);
    		·如果锁已被征用,则返回1;否则返回0
    

    5、示例代码

    struct mutex mutex; 
    int flage = 1; // 1: available     0:busy
    
    static int hello_open (struct inode *inode, struct file *filep)
    {
    	printk("hello_open()\n");
    	mutex_lock(&mutex); //加锁
    	if(flage != 1) //分析一下感觉这个flage可以不要
    				   //这个锁可以用来互斥使用一个全局变量,也可以用来互斥使用一个设备
    				   //不加flage就相当于是保护这个大设备,加flage为互斥使用一个全局变量
    	{
    		mutex_unlock(&mutex); 
    		return -EBUSY;
    	}
    	//occupy device
    	flage = 0;
    //	 
    	return 0;
    }
    static int hello_release (struct inode *inode, struct file *filep)
    {
    	printk("hello_release()\n");
    	//release device
    	flage = 1;
    	mutex_unlock(&mutex);//解锁
    	return 0;
    }
    
    //测试程序
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    main()
    {
    	int fd;
    	int len;
    	char buf[64]={0};
    	char buf2[64+1]="peng";
    	
    	
    	fd = open("/dev/hellodev",O_RDWR);
    	if(fd<0)
    	{
    		perror("open fail \n");
    		return;
    	}
    	printf("before sleep\n");
    	sleep(15);
    	printf("after sleep\n");
    	
    	 
    	close(fd);
    }
    
    

    6、演示效果

    在这里插入图片描述
    在这里插入图片描述


    休眠的时候不能被打断
    在这里插入图片描述
    休眠完成以后才执行信号,休眠期间被阻塞

    展开全文
  • 多线程编程中会涉及公共资源相互竞争问题,就像火车上的厕所,只用一个人可以上,当有人进入后就会加锁。多线程一样为了避免多个线程同时访问公共资源也需要加锁。 一般会用到互斥锁或者自旋锁。 互斥锁线程请求锁...
  • C++——多线程互斥锁

    2018-05-10 15:05:42
    多线程同步的方法:用户模式(原子操作、临界区)、内核模式(互斥量、信号量、事件) 进程间通信方式:管道、信号、消息队列、共享内存、信号量、套接字。 /*以下有两个线程:main和Fun; 通过...
  • 线程互斥锁与死锁

    2017-06-17 15:00:17
    线程访问同一个进程时的临界区时需要被同步与互斥保护避免产生冲突。比如当两个线程都要把某个全局变量增加1,这个操作在某平台上需要三条指令完成:1,从内存读变量到寄存器。2,寄存器的值加1。3,将寄存器...
  • 熟悉Linux的童鞋都对多进程比较熟,Linux下的 fork也有很多教程介绍,但这不是我们本节的重点,我们在这里主要讲的是多线程。  相对于进程来讲,线程 是一个轻量级的概念,一个进程包含多个线程(至少1个)。  ...
  • 一段代码引发的问题 首先,我们来编写一段代码...我们知道,只要是共享的资源,那么它就可以看作临界资源,而临界资源的访问需要同步与互斥机制才能维持正常访问。否则可能会出现数据不一致的问题。 下面,我们来讨.
  • 线程同步与互斥锁

    2019-05-31 18:41:00
    相比多进程模型,多线程模型最大的优势在于数据共享非常方便,同一进程内的多个线程可以使用相同的地址值访问同一块内存数据。但是,当多个线程对同一块内存数据执行“读−处理−更新”操作时,会由于线程的交叉执行...
  • 详解Linux内核5种

    2021-05-10 18:27:30
    Linux内核同步控制方法有很,信号量、、原子量、RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率。本文我们就来谈谈Linux内核5种。Linux作为任务系统,当一个进程生成的数据传输到另一个进程时...
  • Linux利用mutex互斥锁实现线程的互斥

    千次阅读 2017-05-30 17:28:52
    线程对共享数据进行处理时,可能会发生冲突从而导致数据结果与预期结果不一样,导致程序运行错误。 例:在一个主线程中有两个线程,这两个线程都对全局变量num进行+1操作。 注意:上述说的例子可能不会...
  • 目录互斥的理解使用场景使用方法使用原则互斥失败的例子 互斥的理解 一句话理解互斥: 等我用完厕所,你再用厕所。 什么是互斥?你我早起都要用厕所,谁先抢到谁先用,中途不被打扰。 伪代码如下: void 抢厕所(void...
  • 鸿蒙内核源码中文注解 >> 精读内核源码,中文注解分析, 深挖地基工程,大脑永久记忆, 四大源码仓每日同步更新< Gitee | Github | CSDN | Coding > 鸿蒙内核源码分析博客 ...内核中哪些模块会用到互斥锁?看图:
  • POSIX threads(简称...Pthreads提供的机制(lock)来对线程之间的共享临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。 Pthreads提供了多种机制: Mutex(互斥量):pthread_mutex_t...
  • 互斥锁、自旋锁、读写锁和文件锁互斥锁自旋锁...如果释放互斥锁时有一个以上的线程阻塞,那么这些阻塞的线程会被唤醒,它们都会尝试对互斥锁进行加锁,当有一个线程成功对互斥锁上锁之后,其它线程就不能再次上锁了,只
  • C++多线程系列(二)线程互斥

    千次阅读 2016-06-30 20:23:05
    首先了解一下线程互斥的概念,线程互斥说白了就是在进程中个线程的相互制约,如线程A未执行完毕,其他线程就需要等待! 线程之间的制约关系分为间接相互制约和直接相互制约。 所谓间接相互制约:一个系统中的个...
  • 互斥锁一般用来锁多线程都需要访问的临界资源,这些资源不能并发操作,例如对某个内存的互斥读写,这个读写很快,但是不能多线程同时进行,所以需要加互斥锁; 占用互斥锁的线程没能执行,可能是被高优先级线程抢占...
  • 【技术分享篇】epoll的具体实现与epoll线程安全,互斥锁,自旋锁,CAS,原子操作 更Linux服务器开发高阶完整视频分享,点击链接即可观看:https://ke.qq.com/course/417774?flowToken=1013189 内容包括:C/C++,...
  • Linux多线程编程-互斥锁

    万次阅读 2015-07-04 19:11:48
     多线程编程中,(多线程编程)可以用互斥锁(也称互斥量)可以用来保护关键代码段,以确保其独占式的访问,这有点像二进制信号量。POSIX互斥锁相关函数主要有以下5个: #include int pthread_mutex_init(pthread_...
  • UNIX网络编程:互斥锁和条件变量

    千次阅读 2016-05-31 16:30:57
    再进入互斥锁和条件变量之前,我们先对多线程的一些相关函数进行简单介绍:多线程简单介绍和相关函数: 通常,一个进程中包括多个线程,每个线程都是CPU进行调度的基本单位,多线程可以说是在共享内存空间中并发
  • 线程同步之互斥锁

    2018-08-13 07:51:35
    多线程共享相同的内存的时候,需要每一个线程看到相同的视图。当一个线程被修改时,其他的线程也可以修改或者读取这个变量,所以就需要对这些线程同步,保证不会访问到无效的变量。 举个例子: 由此可见,...
  • 互斥锁共享资源的使用是互斥的,即一个线程获得资源的使用权后就会将改资源加锁,使用完后会将其解锁,所以在使用过程中有其它线程想要获取该资源的锁,那么它就会被阻塞陷入睡眠状态,直到该资源被解锁才会别唤醒,...
  • 和多进程程序一样,多线程程序也必须考虑同步问题。线程同步就是一个进程中多个线程协同步调,按预定的先后次序进行运行。pthresd_join可以看作简单的线程同步方式,但是它无法高效地实现复杂的同步需求。 线程同步...
  • 线程间共享数据的方法: 1、全局对象; 2、堆对象(动态创建的...2、互斥锁(Mutex); 临界区和互斥锁的区别:   Mutex Critical Section 性能和速度 慢(Mutex是内核对象,执行相关函数时
  • 互斥量Mutex主要将用到四个函数。下面是这些函数的原型...如果传入TRUE表示互斥量对象内部会记录创建它的线程线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。 * 如果传入FALSE,那
  • 内核线程与用户线程区别、同步互斥的实现原理
  • system v 信号量只能用于进程间同步,而posix 信号量除了可以进程间同步,还可以线程间同步。system v 信号量每次PV操作可以是N,但Posix 信号量每次PV只能是1。除此之外,posix 信号量还有命名和匿名之分(man 7 ...
  • 是什么,我们为什么要用到?回到问题的本质,我们在...对于多线程编程,当两个或多个线程同时访问或对一个临界资源操作的时候,为了防止出现数据不一致的问题,我们在使用之前要进行加锁,使用完后进行解锁。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,876
精华内容 15,550
关键字:

内核多线程互斥锁