精华内容
下载资源
问答
  • Linux线程

    千次阅读 2019-04-23 15:43:32
    Linux线程_01线程介绍和常用系统调用1.Linux线程1.1进程回顾1.2程序并发的时空开销1.3线程的的特点2.POSIX线程常用系统调用2.1线程创建与回收2.2线程取消2.3线程函数退出相关2.4获取线程id 1.Linux线程 如果说在操作...

    1.Linux线程

    如果说在操作系统中引入进程是为了使多个程序可以并发执行,以提高资源利用率和系统吞吐量,那么在操作系统中引入线程则是为了减少程序哎并发执行时所付出的时空开销,使OS具有更好的并发性。

    1.1进程回顾

    • 进程是一个可以拥有资源的独立单位,它所拥有的资源包括用于存放程序的磁盘和内存地址空间以及它在运行时所需要的IO设备、已经打开的文件、信号量等。
    • 传统OS中,进程也是一个可独立调度和分派的基本单位,每个进程在系统中有唯一的PCB,系统可以根据PCB感知进程的存在,咋利用PCB中的信息来恢复进程运行的现场。

    以上两个进程的基本属性使进程成为一个能独立运行的基本单位,也就构成了程序并发执行的基础。

    1.2程序并发的时空开销

    • 1.创建进程:系统创建进程时为其分配其需要的资源。
    • 2.撤销进程:系统撤销进程时回收其所占资源和PCB。
    • 3.进程切换:切换进程时系统会保留当前进程CPU环境,为新进程设置CPU环境。

    进程技术的劣势就是进程间切换开销大,进程间通信麻烦而且效率低

    1.3线程的的特点

    与进程(process)类似,线程(thread)是允许程序并发执行多个任务的一种机制。一个进程可以包含多个线程,统一程序中的每个线程都可以独立的执行相同程序。(传统意义上的UNIX进程只是多线程程序的特例,该进程只包含一个线程)在引入线程的OS中,线程作为调度和分派的基本单位,可以有效地改善多处理机系统性能。线程具有像进程一样可被OS调度同一进程的多个线程之间很容易高效率通信在多核心CPU(对称多处理器架构SMP)架构下效率最大化等优势。

    • 同一进程中的线程可以并发执行。
    • 线程本身并不拥有资源,而是仅有一点必不可少的、能保证其独立运行的资源,如用于控制线程运行的线程控制块TCB等。
    • 同一进程中的线程共享进程的内存地址空间和资源。
    • 线程也有三种状态:执行状态、就绪状态、阻塞状态。

    2.POSIX线程常用系统调用

    启动程序时,产生的进程只有单条线程,称为初始线程或主线程

    2.1线程创建与回收

    • pthread_create() 创造子线程
    #include <pthread.h>
    int pthread_create(pthread_t *thread, 
    				   const pthread_attr_t *attr,
                       void *(*start_routine) (void *), 
                       void *arg);
    

    其中thread为指向线程标识符的指针,attr用来设置线程属性,剩下的两个参数分别是该线程执行的函数和函数的参数。

    • pthread_join() 等待(阻塞)回收子线程
    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);
    

    thread为被等待的线程标识符,retval为一个用户定义的指针,它可以用来存储被等待线程的返回值。

    • pthread_detach() 分离子线程,分离后主线程不必再去回收子线程
    #include <pthread.h>
    int pthread_detach(pthread_t thread);
    

    thread为被分离的子线程的标识符。

    pthread_join()和pthread_detach()不能同时都选,也不能同时都不选。

    2.2线程取消

    取消就是强制终止线程。

    • pthread_cancel() 取消子线程
    #include <pthread.h>
    int pthread_cancel(pthread_t thread);
    

    thread为被取消的子线程的标识符。

    • pthread_setcancelstate()和 pthread_setcanceltype() 子线程设置自己是否允许被取消
    #include <pthread.h>
    int pthread_setcancelstate(int state, int *oldstate);
    int pthread_setcanceltype(int type, int *oldtype);
    

    state参数有两个选项PTHREAD_CANCEL_ENABLE、PTHREAD_CANCEL_DISABLE分别是可以取消和不可取消。pthread_setcanceltype()在state是enable的情况下才有意义,type对于两个参数PTHREAD_CANCEL_DEFERRED子线程不能立即终止和PTHREAD_CANCEL_ASYNCHRONOUS子线程任何时候都立即终止。

    2.3线程函数退出相关

    • pthread_exit()或函数里return返回指定值 线程正常退出
    #include <pthread.h>
    void pthread_exit(void *retval);
    
    • pthread_cleanup_push()
    #include <pthread.h>
    void pthread_cleanup_push(void (*routine)(void *),void *arg);
    
    • pthread_cleanup_pop()
    #include <pthread.h>
    void pthread_cleanup_pop(int execute);
    

    2.4获取线程id

    • pthread_self()
    #include <pthread.h>
    pthread_t pthread_self(void);
    
    展开全文
  • Linux 线程

    2016-08-24 17:43:29
    Linux线程是针对POSIX线程,也就是pthread,Linux对它的支持最好。  线程是一个更接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序的基础上引入...
       线程(thread)是在 共享内存空间 中并发的多道执行路径,它们共享一个进程资源,如文件描述符和信号处理。Linux的线程是针对POSIX线程,也就是pthread,Linux对它的支持最好。
    
       线程是一个更接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序的基础上引入线程和进程是为了提高程序的并发度,从而提高程序的运行效率和响应时间。
       也可以将线程和轻量级进程(LWP)视为同等,但LWP更恰当的解释为一个虚拟CPU或内核的线程。它可以帮助用户态线程实现一些特殊的功能。
       Pthread是一种标准化模型,他把一个程序分成一组能够执行的任务。
    一般,下列三种场合会使用Pthread线程:
    • 在返回前 阻塞的I/O任务 能够使用一个线程处理I/O, 同时处理其他任务。
    • 在有一个或多个任务受不确定性事件影响,比如网络通信的可获得性影响的场合,能够使用线程处理这些异步事件,同时执行正常的处理。
    • 如果程序的某些功能比其他功能更重要,可以使用线程以保证所有功能都出现。但那些时间密集型的功能具有更高的优先级。 
      总结: 在检查程序中潜在的并行性时,也就是说在要找出能够同时执行任务时使用Pthread
     
      Linux进程模型提供了执行多个进程的能力,已经可以进行并行或者并发编程。可是,线程能够让你对多个任务的控制度更好、使用资源更少,因为一个单一的资源,如全局变量,可以由多个线程共享。而且,在拥有多个处理器的系统上。多线程应该比用多个进程实现的应用的执行速度更快!
        线程和进程在使用上各有优缺点:
    • 线程执行开销小,但不利于资源的管理和保护;而进程正相反。
    • 线程适合在对称多处理器的计算机上运行,而进程则可以跨机器迁移;
    • 进程可以拥有资源,而线程共享进程拥有的资源;
    • 进程的切换必须保存在进程控制块中(PCB,Process Control Block),同个进程的多个线程间的切换不用那么麻烦。
            
    总结:
         进程单独占有系统资源,进程的切换必须保存在进程控制块(PCB)中;线程共享进程的资源,(同个进程的)线程之间的切换不那么麻烦。线程是一个更接近于执行体的概念,它可以同进程中的其他线程共享数据,但拥有自己的栈空间拥有独立的执行序列



    展开全文
  • Linux 线程锁详解

    千次阅读 2018-11-08 00:50:54
    Linux 线程锁详解

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                    在Posix Thread中定义有一套专门用于线程同步的mutex函数。
      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_destroy ()用于注销一个互斥锁,API定义如下: int pthread_mutex_destroy(pthread_mutex_t *mutex) 销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。
      2. 互斥锁属性
      互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:
      * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
      * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
      * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
      * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
      3. 锁操作
      锁操作主要包括加锁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而不是挂起等待。
      4. 其他
      POSIX 线程锁机制的Linux实现都不是取消点,因此,延迟取消类型的线程不会因收到取消信号而离开加锁等待。值得注意的是,如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,或者设置了异步取消类型,则必须在退出回调函数中解锁。
      这个锁机制同时也不是异步信号安全的,也就是说,不应该在信号处理过程中使用互斥锁,否则容易造成死锁。
      互斥锁属性使用互斥锁(互斥)可以使线程按顺序执行。通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程。互斥锁还可以保护单线程代码。
      要更改缺省的互斥锁属性,可以对属性对象进行声明和初始化。通常,互斥锁属性会设置在应用程序开头的某个位置,以便可以快速查找和轻松修改。表 4–1 列出了用来处理互斥锁属性的函数。
      表 4–1 互斥锁属性例程
    操作相关函数说明
    初始化互斥锁属性对象pthread_mutexattr_init 语法
    销毁互斥锁属性对象pthread_mutexattr_destroy 语法
    设置互斥锁范围pthread_mutexattr_setpshared 语法
    获取互斥锁范围pthread_mutexattr_getpshared 语法
    设置互斥锁的类型属性pthread_mutexattr_settype 语法
    获取互斥锁的类型属性pthread_mutexattr_gettype 语法
    设置互斥锁属性的协议pthread_mutexattr_setprotocol 语法
    获取互斥锁属性的协议pthread_mutexattr_getprotocol 语法
    设置互斥锁属性的优先级上限pthread_mutexattr_setprioceiling 语法
    获取互斥锁属性的优先级上限pthread_mutexattr_getprioceiling 语法
    设置互斥锁的优先级上限pthread_mutex_setprioceiling 语法
    获取互斥锁的优先级上限pthread_mutex_getprioceiling 语法
    设置互斥锁的强健属性pthread_mutexattr_setrobust_np 语法
    获取互斥锁的强健属性pthread_mutexattr_getrobust_np 语法
     表 4–2 中显示了在定义互斥范围时 Solaris 线程和 POSIX 线程之间的差异。
      表 4–2 互斥锁范围比较
    SolarisPOSIX定义
    USYNC_PROCESSPTHREAD_PROCESS_SHARED用于同步该进程和其他进程中的线程
    USYNC_PROCESS_ROBUST无 POSIX 等效项用于在进程间可靠地同步线程
    USYNC_THREADPTHREAD_PROCESS_PRIVATE用于仅同步该进程中的线程
    初始化互斥锁属性对象  使用 pthread_mutexattr_init(3C)可以将与互斥锁对象相关联的属性初始化为其缺省值。在执行过程中,线程系统会为每个属性对象分配存储空间。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 的类型为 opaque,其中包含一个由系统分配的属性对象。mattr 范围可能的值为 PTHREAD_PROCESS_PRIVATE 和 PTHREAD_PROCESS_SHARED。PTHREAD_PROCESS_PRIVATE 是缺省值。
      对于互斥锁属性对象,必须首先通过调用 pthread_mutexattr_destroy(3C) 将其销毁,才能重新初始化该对象。pthread_mutexattr_init()调用会导致分配类型为 opaque 的对象。如果未销毁该对象,则会导致内存泄漏。pthread_mutexattr_init 返回值  pthread_mutexattr_init()成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
      ENOMEM
      描述: 内存不足,无法初始化互斥锁属性对象。
    销毁互斥锁属性对象  pthread_mutexattr_destroy(3C) 可用来取消分配用于维护pthread_mutexattr_init() 所创建的属性对象的存储空间。 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 返回值  pthread_mutexattr_destroy() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
      EINVAL
      描述: 由 mattr 指定的值无效。
    设置互斥锁的范围  pthread_mutexattr_setpshared(3C) 可用来设置互斥锁变量的作用域。pthread_mutexattr_setpshared 语法  int pthread_mutexattr_setpshared(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);互斥锁变量可以是进程专用的(进程内)变量,也可以是系统范围内的(进程间)变量。要在多个进程中的线程之间共享互斥锁,可以在共享内存中创建互斥锁,并将pshared 属性设置为 PTHREAD_PROCESS_SHARED。 此行为与最初的 Solaris 线程实现中 mutex_init() 中的 USYNC_PROCESS 标志等效。
      如果互斥锁的 pshared 属性设置为 PTHREAD_PROCESS_PRIVATE,则仅有那些由同一个进程创建的线程才能够处理该互斥锁。pthread_mutexattr_setpshared 返回值  pthread_mutexattr_setpshared() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
      EINVAL
      描述: 由 mattr 指定的值无效。
    获取互斥锁的范围  pthread_mutexattr_getpshared(3C) 可用来返回由pthread_mutexattr_setpshared() 定义的互斥锁变量的范围。 pthread_mutexattr_getpshared 语法  int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int *pshared);#include <pthread.h>pthread_mutexattr_t mattr;int pshared, ret;/* get pshared of mutex */ret = pthread_mutexattr_getpshared(&mattr, &pshared); 此函数可为属性对象 mattr 获取 pshared 的当前值。该值为 PTHREAD_PROCESS_SHARED 或 PTHREAD_PROCESS_PRIVATE。pthread_mutexattr_getpshared 返回值  pthread_mutexattr_getpshared() 成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
      EINVAL
      描述: 由 mattr 指定的值无效。
    设置互斥锁类型的属性  pthread_mutexattr_settype(3C) 可用来设置互斥锁的type 属性。 pthread_mutexattr_settype 语法  #include <pthread.h>int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。
      type 参数指定互斥锁的类型。以下列出了有效的互斥锁类型:
      PTHREAD_MUTEX_NORMAL
      描述: 此类型的互斥锁不会检测死锁。如果线程在不首先解除互斥锁的情况下尝试重新锁定该互斥锁,则会产生死锁。尝试解除由其他线程锁定的互斥锁会产生不确定的行为。如果尝试解除锁定的互斥锁未锁定,则会产生不确定的行为。
      PTHREAD_MUTEX_ERRORCHECK
      描述: 此类型的互斥锁可提供错误检查。如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则会返回错误。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
      PTHREAD_MUTEX_RECURSIVE
      描述: 如果线程在不首先解除锁定互斥锁的情况下尝试重新锁定该互斥锁,则可成功锁定该互斥锁。 与 PTHREAD_MUTEX_NORMAL 类型的互斥锁不同,对此类型互斥锁进行重新锁定时不会产生死锁情况。多次锁定互斥锁需要进行相同次数的解除锁定才可以释放该锁,然后其他线程才能获取该互斥锁。如果线程尝试解除锁定的互斥锁已经由其他线程锁定,则会返回错误。 如果线程尝试解除锁定的互斥锁未锁定,则会返回错误。
      PTHREAD_MUTEX_DEFAULT
      描述: 如果尝试以递归方式锁定此类型的互斥锁,则会产生不确定的行为。对于不是由调用线程锁定的此类型互斥锁,如果尝试对它解除锁定,则会产生不确定的行为。对于尚未锁定的此类型互斥锁,如果尝试对它解除锁定,也会产生不确定的行为。允许在实现中将该互斥锁映射到其他互斥锁类型之一。对于 Solaris 线程,PTHREAD_PROCESS_DEFAULT 会映射到 PTHREAD_PROCESS_NORMAL。pthread_mutexattr_settype 返回值  如果运行成功,pthread_mutexattr_settype 函数会返回零。否则,将返回用于指明错误的错误号。
      EINVAL
      描述: 值为 type 无效。
      EINVAL
      描述: attr 指定的值无效。
    获取互斥锁的类型属性  pthread_mutexattr_gettype(3C) 可用来获取由pthread_mutexattr_settype() 设置的互斥锁的 type 属性。 pthread_mutexattr_gettype 语法  #include <pthread.h>int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。
      type 参数指定互斥锁的类型。有效的互斥锁类型包括:
      PTHREAD_MUTEX_NORMAL
      PTHREAD_MUTEX_ERRORCHECK
      PTHREAD_MUTEX_RECURSIVE
      PTHREAD_MUTEX_DEFAULT
      有关每种类型的说明,请参见pthread_mutexattr_settype 语法。 pthread_mutexattr_gettype 返回值  如果成功完成,pthread_mutexattr_gettype() 会返回 0。其他任何返回值都表示出现了错误。
    设置互斥锁属性的协议  pthread_mutexattr_setprotocol(3C) 可用来设置互斥锁属性对象的协议属性。pthread_mutexattr_setprotocol 语法  #include <pthread.h>int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);attr 指示以前调用pthread_mutexattr_init() 时创建的互斥锁属性对象。
      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 可以避免优先级倒置。低优先级的线程持有较高优先级线程所需的锁时,便会发生优先级倒置。只有在较低优先级的线程释放该锁之后,较高优先级的线程才能继续使用该锁。设置 PTHREAD_PRIO_INHERIT,以便按与预期的优先级相反的优先级处理每个线程。
      如果为使用协议属性值 PTHREAD_PRIO_INHERIT 初始化的互斥锁定义了 _POSIX_THREAD_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
      当线程拥有一个或多个使用 PTHREAD_PRIO_PROTECT 初始化的互斥锁时,此协议值会影响其他线程(如 thrd2)的优先级和调度。thrd2 以其较高的优先级或者以 thrd2 拥有的所有互斥锁的最高优先级上限运行。基于被 thrd2 拥有的任一互斥锁阻塞的较高优先级线程对于 thrd2 的调度没有任何影响。
      如果某个线程调用 sched_setparam() 来更改初始优先级,则调度程序不会采用新优先级将该线程移到调度队列末尾。
      线程拥有使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥锁
      线程解除锁定使用 PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT 初始化的互斥锁
      一个线程可以同时拥有多个混合使用 PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 初始化的互斥锁。在这种情况下,该线程将以通过其中任一协议获取的最高优先级执行。pthread_mutexattr_setprotocol 返回值  如果成功完成,pthread_mutexattr_setprotocol() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下任一情况,pthread_mutexattr_setprotocol() 将失败并返回对应的值。
      ENOSYS
      描述: 选项 _POSIX_THREAD_PRIO_INHERIT 和 _POSIX_THREAD_PRIO_PROTECT 均未定义并且该实现不支持此函数。
      ENOTSUP
      描述: protocol 指定的值不受支持。
      如果出现以下任一情况,pthread_mutexattr_setprotocol() 可能会失败并返回对应的值。
      EINVAL
      描述: attr 或 protocol 指定的值无效。
      EPERM
      描述: 调用方无权执行该操作。
    获取互斥锁属性的协议  pthread_mutexattr_getprotocol(3C) 可用来获取互斥锁属性对象的协议属性。pthread_mutexattr_getprotocol 语法  #include <pthread.h>int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);attr 指示以前调用 pthread_mutexattr_init() 时创建的互斥锁属性对象。
      protocol 包含以下协议属性之一:PTHREAD_PRIO_NONE、PTHREAD_PRIO_INHERIT 或 PTHREAD_PRIO_PROTECT。pthread_mutexattr_getprotocol 返回值  如果成功完成,pthread_mutexattr_getprotocol() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下情况,pthread_mutexattr_getprotocol() 将失败并返回对应的值。
      ENOSYS
      描述: _POSIX_THREAD_PRIO_INHERIT 选项和 _POSIX_THREAD_PRIO_PROTECT 选项均未定义并且该实现不支持此函数。
      如果出现以下任一情况,pthread_mutexattr_getprotocol() 可能会失败并返回对应的值。
      EINVAL
      描述: attr 指定的值无效。
      EPERM
      描述: 调用方无权执行该操作。
    设置互斥锁属性的优先级上限  pthread_mutexattr_setprioceiling(3C) 可用来设置互斥锁属性对象的优先级上限属性。pthread_mutexattr_setprioceiling 语法  #include <pthread.h>int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling, int *oldceiling);attr 指示以前调用 pthread_mutexattr_init() 时创建的互斥锁属性对象。
      prioceiling 指定已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。
      oldceiling 包含以前的优先级上限值。 pthread_mutexattr_setprioceiling 返回值  如果成功完成,pthread_mutexattr_setprioceiling() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下任一情况,pthread_mutexattr_setprioceiling() 将失败并返回对应的值。
      ENOSYS
      描述: 选项 _POSIX_THREAD_PRIO_PROTECT 未定义并且该实现不支持此函数。
      如果出现以下任一情况,pthread_mutexattr_setprioceiling() 可能会失败并返回对应的值。
      EINVAL
      描述: attr 或 prioceiling 指定的值无效。
      EPERM
      描述: 调用方无权执行该操作。
    获取互斥锁属性的优先级上限  pthread_mutexattr_getprioceiling(3C) 可用来获取互斥锁属性对象的优先级上限属性。pthread_mutexattr_getprioceiling 语法  #include <pthread.h>int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *attr, int *prioceiling);attr 指定以前调用 pthread_mutexattr_init() 时创建的属性对象。
      注 –
      仅当定义了 _POSIX_THREAD_PRIO_PROTECT 符号时,attr 互斥锁属性对象才会包括优先级上限属性。
      pthread_mutexattr_getprioceiling() 返回 prioceiling 中已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。pthread_mutexattr_getprioceiling 返回值  如果成功完成,pthread_mutexattr_getprioceiling() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下任一情况,pthread_mutexattr_getprioceiling() 将失败并返回对应的值。
      ENOSYS
      描述: _POSIX_THREAD_PRIO_PROTECT 选项未定义并且该实现不支持此函数。
      如果出现以下任一情况,pthread_mutexattr_getprioceiling() 可能会失败并返回对应的值。
      EINVAL
      描述: attr 指定的值无效。
      EPERM
      描述: 调用方无权执行该操作。
    设置互斥锁的优先级上限  pthread_mutexattr_setprioceiling(3C) 可用来设置互斥锁的优先级上限。pthread_mutex_setprioceiling 语法  #include <pthread.h>int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling);pthread_mutex_setprioceiling() 可更改互斥锁 mutex 的优先级上限 prioceiling。 pthread_mutex_setprioceiling() 可锁定互斥锁(如果未锁定的话),或者一直处于阻塞状态,直到pthread_mutex_setprioceiling() 成功锁定该互斥锁,更改该互斥锁的优先级上限并将该互斥锁释放为止。锁定互斥锁的过程无需遵循优先级保护协议。
      如果 pthread_mutex_setprioceiling() 成功,则将在 old_ceiling 中返回以前的优先级上限值。如果pthread_mutex_setprioceiling() 失败,则互斥锁的优先级上限保持不变。 pthread_mutex_setprioceiling 返回值  如果成功完成,pthread_mutex_setprioceiling() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下情况,pthread_mutexatt_setprioceiling() 将失败并返回对应的值。
      ENOSYS
      描述: 选项_POSIX_THREAD_PRIO_PROTECT 未定义并且该实现不支持此函数。
      如果出现以下任一情况,pthread_mutex_setprioceiling() 可能会失败并返回对应的值。
      EINVAL
      描述: prioceiling 所请求的优先级超出了范围。
      EINVAL
      描述: mutex 指定的值不会引用当前存在的互斥锁。
      ENOSYS
      描述: 该实现不支持互斥锁的优先级上限协议。
      EPERM
      描述: 调用方无权执行该操作。
    获取互斥锁的优先级上限  pthread_mutexattr_getprioceiling(3C) 可用来获取互斥锁的优先级上限。pthread_mutex_getprioceiling 语法  #include <pthread.h>int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling);pthread_mutex_getprioceiling() 会返回 mutex 的优先级上限 prioceiling。 pthread_mutex_getprioceiling 返回值  如果成功完成,pthread_mutex_getprioceiling() 会返回 0。其他任何返回值都表示出现了错误。
      如果出现以下任一情况,pthread_mutexatt_getprioceiling() 将失败并返回对应的值。
      ENOSYS
      描述: _POSIX_THREAD_PRIO_PROTECT 选项未定义并且该实现不支持此函数。
      如果出现以下任一情况,pthread_mutex_getprioceiling() 可能会失败并返回对应的值。
      EINVAL
      描述: mutex 指定的值不会引用当前存在的互斥锁。
      ENOSYS
      描述: 该实现不支持互斥锁的优先级上限协议。
      EPERM
      描述: 调用方无权执行该操作。
    设置互斥锁的强健属性  pthread_mutexattr_setrobust_np(3C) 可用来设置互斥锁属性对象的强健属性。pthread_mutexattr_setrobust_np 语法  #include <pthread.h>int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness);注 –
      仅当定义了符号 _POSIX_THREAD_PRIO_INHERIT 时,pthread_mutexattr_setrobust_np() 才适用。
      attr 指示以前通过调用 pthread_mutexattr_init() 创建的互斥锁属性对象。
      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
      互斥锁的属主失败时,将会解除锁定该互斥锁。互斥锁的下一个属主将获取该互斥锁,并返回错误 EOWNWERDEAD。
      注 – 应用程序必须检查 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() 将失败并返回对应的值。
      ENOSYS
      描述: 选项 _POSIX_THREAD_PRIO__INHERIT 未定义,或者该实现不支持 pthread_mutexattr_setrobust_np()
      ENOTSUP
      描述: robustness 指定的值不受支持。
      pthread_mutexattr_setrobust_np() 可能会在出现以下情况时失败:
      EINVAL
      描述: attr 或 robustness 指定的值无效。           

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • linux线程及线程同步(锁的应用)

    千次阅读 2018-06-08 15:36:33
    linux线程 linux原本没有线程,后来在windows多线程编程影响下linux内核开发者在进程基础上在功能上做出了类似windows线程的linux版本的线程,linux线程归根到底还是进程,只不过是轻量级的进程,开销比真正进程...

    linux线程

    linux原本没有线程,后来在windows多线程编程影响下linux内核开发者在进程基础上在功能上做出了类似windows线程的linux版本的线程,linux线程归根到底还是进程,只不过是轻量级的进程,开销比真正进程要小得多,大家还是要明白linuxwindows在线程方面功能虽然类似,但是底层实现是非常不同的。


    linux进程大概实现原理

    在进程的基础上创建线程,原本进程PCB将会随着线程数量的增加被均分,原本进程的主人将会退化成主线程,但是这些线程所有的PCB所占总和相当于原来进程PCB空间。内核在任务切换时只识别PCB因此线程在效率上相比进程没有什么优势,对于内核而言线程就是进程,唯一的好处就是线程之间通信、同步比进程要方便的多,进程间共享的资源也更多,在系统开销方面也更小,在编写程序时能够更加灵活,例如原来全局变量父子进程不能共享,在线程上线程之间可以共享全局变量,但是有利有弊,像原来进程中经常使用的perror exit函数在多线程内并不能随意使用,如果某个线程调用exit函数,就会像原来杀死进程一样,杀死PCB中所有线程,杀伤范围太大


    资源分配

    主线程和子线程
    共享:

    • .text
    • .bss
    • .data
    • 动态库加载区
    • 环境变量
    • 命令行参数

    进程通信:可采取全局变量、堆的形式
    不共享:

    • 栈(每个线程栈区是独立的,记录着每个线程各自独立的栈信息,因此线程通信不能通过局部变量进行通信)

    多进程和多线程的区别

    • 始终共享的资源
      • 代码
      • 文件描述符
      • 内存映射区 --mmap
    • 线程共享
      • 全局变量
    • 线程节省资源
    • 在编译时需要在编译选项中增加-lpthread,调用标准线程库

    线程操作相关函数

    获得线程号

    #include <pthread.h>
    pthread_t pthread_self(void);

    线程号比较大,打印输出或者相关操作可采取长整型对应付

    创建进程

    #include <pthread.h>
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                      void *(*start_routine) (void *), void *arg);
    • 参数说明:
      • thread:线程号的传出参数,用来存储线程号,类型为·pthread_t *·类型
      • attr:线程属性无高级用法情况下填写NULL即可
      • void (*start_routine) (void ):看起来比较复杂其实是一个参数为void *类型,返回值为void *类型的函数指针
      • arg:是第三个函数的具体参数
    • 返回值:
      • 成功返回0,失败返回错误号
      • perror()不能使用该函数打印错误信息
        注意:
    • 主线程先退出(使用exitreturn形式),子线程会被强制结束
      下面是程序
    #include<pthread.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    void *func(void* arg)
    {
        int i;
        printf("this is son thread\n");
        for(i=0;i<5;i++)
        {
            printf("i is %d\n",i);
            sleep(1);
        }
    
    }
    int main()
    {
        pthread_t id;
        pthread_create(&id,NULL,func,NULL);
        printf("son thread id is %ld,father thread is %ld\n",id,pthread_self());
        sleep(2);
        return 0;
    }

    通过程序运行结果,我们可以看到父线程结束退出时,尽管子线程还没运行结束,但是随着父线程的结束子线程被迫结束了,

    root@ThinkPad:/home/image/mycode/linux/pthread# ./mypthread 
    son thread id is 140527533901568,father thread is 140527542400832
    this is son thread
    i is 0
    i is 1
    • 线程共享全局变量(小例程)
    #include<pthread.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/wait.h>
    int num;
    void *func(void* arg)
    {
        int i;
        for(i=0;i<5;i++)
        {
            num++;
            printf("son: num is %d\n",num);
            sleep(1);
        }
    
    }
    int main()
    {
        pthread_t id;
        pthread_create(&id,NULL,func,NULL);
        for(int i=0;i<5;i++)
        {
            num++;
            printf("father:num is %d\n",num);
            sleep(2);
        }
        return 0;
    }

    程序运行结果显示父子线程对全局变量的操作能影响对方,说明全局变量他们是共享的

    root@ThinkPad:/home/image/mycode/linux/pthread# ./mypthread 
    father:num is 1
    son: num is 2
    son: num is 3
    father:num is 4
    son: num is 5
    son: num is 6
    father:num is 7
    son: num is 8
    father:num is 9
    father:num is 10

    线程打印错误信息

    在进程里面,根据函数返回值,判断某些操作执行失败,然后调用perror函数就可以打印错误信息,但是多线程挤在同一区域,错误信息各个线程是共享的即使有错误发生,打印出来错误信息,也不见得是本个线程自身的错误信息可能是别的线程有错误发生还没来得及打印,被别的线程抢先打印了,所以线程的错误信息不能调用perror函数打印,那么怎样打印线程的错误信息呢?,我们查看pthread_create函数帮助手册,手册在返回值这章介绍说,如果函数执行成功则返回0,执行失败则返回错误号,打印错误信息我们可以通过错误号去进一步寻找错误发生的具体原因,由于错误号各个线程是相互独立的,所以根据错误号检索出来的错误信息就跟线程对应上了。具体做法如下:
    使用函数:

     #include <string.h>
     char *strerror(int errnum);

    参数说明:

    • errnum:错误号

    函数返回值

    • char* :错误号对应错误信息

    单个线程退出

     #include <pthread.h>
     void pthread_exit(void *retval);
    • 参数说明
      • retval: void *类型,这个参数就是线程结束时向外传递的信息,例如进程结束exit(0),参数0其实就是进程退出时的返回值,我们使用waitpid函数时,如果设置了进程退出状态的参数,这个参数打印出来就对应exit传入的数值,retval这个参数同理,可以让别的线程获得自己结束时的相关信息,需要说明的是,这个参数是指针类型,不能使用栈指针,因为线程结束对应栈信息被回收,所以这个参数必须指向全局变量或者堆
    • 返回值

    注意:
    线程退出一定不要用exitexit针对进程,如果在线程调用,那么在原来进程空间里所有的线程将都会被干掉,杀伤范围太大。

    #include<pthread.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    void *func(void *arg)
    {
        int i;
        for(i=0;i<5;i++)
        {
            printf("son i is %d\n",i);
            sleep(1);
        }
    }
    
    int main(int argc,char *argv[])
    {
        pthread_t pid;
        pthread_create(&pid,NULL,func,NULL);
        pthread_exit(NULL);
        for(int i=0;i<5;i++)
        {
            printf("father:i is %d\n",i);
            sleep(1);
        }
        return 0;
    }

    程序运行结果表明,父线程打印部分没有执行,证明父线程提前结束,福线程退出没有造成子线程强制退出,这是因为父线程退出行为不是由return或者exit造成,杀伤范围没有那么大,。没有造成子线程的退出

    root@thinkpad:/softcode/linux/pthread# ./mypthread 
    son i is 0
    son i is 1
    son i is 2
    son i is 3
    son i is 4

    阻塞等待线程退出函数

     #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);

    函数参数:

    • thread:线程号
    • retval:线程退出时的传出参数,这个参数在这里是个传出参数,二级指针,指向内存和pthread_exit函数指向内存是一致的
      一般用法如下:
    void *ptr;
    pthread_join(thread,&ptr);

    小例程

    #include<pthread.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    int num=100;
    void *func(void *arg)
    {
        int i;
        for(i=0;i<5;i++)
        {
            if(i==2)
                pthread_exit(&num);
            printf("son i is %d\n",i);
            sleep(1);
        }
    }
    
    int main(int argc,char *argv[])
    {
        pthread_t pid;
        pthread_create(&pid,NULL,func,NULL);
        void *ptr;
        pthread_join(pid,&ptr);
        printf("ptr is %d\n",*(int *)ptr);
        for(int i=0;i<5;i++)
        {
            printf("father:i is %d\n",i);
            sleep(1);
        }
        return 0;
    }

    程序运行结果显示父线程等待子线程退出后,在执行父线程以后的代码,说明这个函数是阻塞性质的,而且获得了指定线程的退出时对外传递的相关信息

    root@thinkpad:/softcode/linux/pthread# ./mypthread 
    son i is 0
    son i is 1
    ptr is 100
    father:i is 0
    father:i is 1
    father:i is 2
    father:i is 3
    father:i is 4
    

    线程分离

    #include <pthread.h>
    int pthread_detach(pthread_t thread);

    作用:可以指定线程结束时自己回收自己的资源,不用再使用pthread_join函数
    参数:

    • 线程号

    返回值:

    • 成功返回0
    • 失败返回错误号

    在创建线程就设置分离属性

    在极端情况下有时候创建了线程,还没为其设置分离属性,线程就退出了,所以我们可以在线程创建时就可以其创建分离属性,具体方法如下:
    前面在线程创建时属性参数设置为NULL,这里我们可以利用这个属性为线程设置分离属性

    • 创建线程属性变量:pthread_attr_t attr
    • 对线程属性变量进行初始化
      int pthread_attr_init(pthread_attr_t *attr)
    • 设置线程分离属性
      int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate)
      参数:
      attr :线程属性变量
      detachstate:
      PTHREAD_CREATE_DETACHED(分离)
      PTHREAD_CREAT_JOINABLE(非分离)
    • 释放线程属性占用资源函数
      int pthread_attr_destroy(pthread_attr_t *attr)

    杀死线程

    #include <pthread.h>
    int pthread_cancel(pthread_t thread);

    作用:杀死指定线程
    参数:

    • 线程号

    返回值:

    • 成功返回0
    • 失败返回错误号

    注意:
    被指定要杀死的线程必须执行一次系统调用才能被杀死,如果线程内部处理函数没有系统调用函数,该线程不能被杀死,如果必须要结束该线程,可调用pthread_testcancel()函数,该函数无实际意义,但是执行的是系统调用,可以作为线程线程结束点。

    #include<pthread.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    int num=100;
    void *func(void *arg)
    {
        int i;
        for(i=0;i<5;i++)
        {
            printf("son i is %d\n",i);
            sleep(1);
        }
    }
    
    int main(int argc,char *argv[])
    {
        pthread_t pid;
        pthread_create(&pid,NULL,func,NULL);
        void *ptr;
        sleep(2);
        pthread_cancel(pid);
       // pthread_join(pid,&ptr);
        //printf("ptr is %d\n",*(int *)ptr);
        for(int i=0;i<5;i++)
        {
            printf("father:i is %d\n",i);
            sleep(1);
        }
        pthread_exit(NULL);
    }
    
    

    子线程执行两次就被父线程杀死,因为子线程存在printf函数,执行时调用内核sprintf,所以存在线程终止点,子线程被顺利杀死

    root@thinkpad:/softcode/linux/pthread# ./mypthread 
    son i is 0
    son i is 1
    father:i is 0
    father:i is 1
    father:i is 2
    father:i is 3
    father:i is 4
    

    线程同步

    为了说明线程同步的意义,我们先看一个小例程,该例程无实际意义,只是为了放大要说明的问题

    #include<pthread.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #define MAX 10000
    int num=0;
    void *func1(void *arg)
    {
        int i;
        for(i=0;i<MAX;i++)
        {
            int cur;
            cur=num;
            cur++;
            usleep(10);
            num=cur;
            printf("ID is %ld,func1:num is %d\n",pthread_self(),num);
        }
        return NULL;
    }
    void *func2(void *arg)
    {
        int i;
        for(i=0;i<MAX;i++)
        {
            int cur;
            cur=num;
            cur++;
            usleep(10);
            num=cur;
            printf("ID is %ld,func2:num is %d\n",pthread_self(),num);
    
        }
        return NULL;
    }
    
    
    int main(int argc,char *argv[])
    {
        pthread_t pid1,pid2;
        pthread_create(&pid1,NULL,func1,NULL);
        pthread_create(&pid2,NULL,func2,NULL);
    
        pthread_join(pid1,NULL);
        pthread_join(pid2,NULL);
        return 0;
    }

    上面的例程中,每个线程都对全局变量num自加一万次,因此理论上num值最终应该是20000,但是程序运行结果却不是这样,下面是运行结果:

    ID is 140548474279680,func2:num is 10001
    ID is 140548474279680,func2:num is 10002
    ID is 140548482672384,func1:num is 10002
    ID is 140548482672384,func1:num is 10003
    ID is 140548474279680,func2:num is 10003
    ID is 140548474279680,func2:num is 10004
    ID is 140548482672384,func1:num is 10004
    ID is 140548482672384,func1:num is 10005
    ID is 140548474279680,func2:num is 10005
    ID is 140548482672384,func1:num is 10006
    ID is 140548474279680,func2:num is 10006
    ID is 140548482672384,func1:num is 10007
    ID is 140548474279680,func2:num is 10007
    ID is 140548482672384,func1:num is 10008
    ID is 140548482672384,func1:num is 10009
    root@thinkpad:/softcode/linux/pthread# 

    这是因为,当一个线程运行时,虽然for循环中i的值已经执行过了,但在for循环体中执行相关操作时CPU被抢夺了,赋值动作未完成,另一个线程取得的num值并不是经过增加num值,虽然i的值有序增加,但num因各种阴差阳错并没有真正累加到自身。造成了数据最后偏小的缘故,这样就出现了一个很现实的问题,就是对共享资源的保护问题,这就需要线程执行时要考虑同步问题

    线程同步思想

    锁:多个线程对同一资源进行访问时,为确保每个线程对资源操作有效性,线程拿到资源时对资源加锁,其他线程不能对其进行操作,对资源操作结束后,开锁其他线程可以利用,使线程并发执行变成有序的顺序执行,使步调协调。


    互斥锁

    • 互斥锁的类型
      创建一把锁:pthread_mutex_t mutex;
    • 互斥锁的特点
      • 多个线程访问共享数据的时候是串行的
    • 使用互斥锁缺点?
      • 效率低
    • 互斥锁使用步骤:
      • 创建互斥锁
      • 初始化互斥锁
      • 在线程访问共享数据处加锁
      • 线程访问共享数据结束后解锁
      • 所有线程使用完成后记得销毁锁
    • 互斥锁相关函数

      • 初始化互斥锁
      pthread_mutex_init(pthread_mutex_t *restrict mutex,
                          consst pthread_mutexattr_t *restrict attr)
      • 销毁互斥锁
      pthread_mutex_destroy(pthread_mutex_t *restrict mutex)
      • 加锁
      pthread_mutex_lock(pthread_mutex_t *restrict mutex);
      
        • mutex
          没有被上锁,当前线程会把这把锁锁上
          被锁上了:当前线程阻塞
          锁打开后线程解除阻塞
      • 尝试加锁
      pthread_mutex_trylock(pthread_mutex_t *restrict mutex);
      
        • 没有锁,当前线程会给这把锁加锁,返回0
          锁上了,线程不会阻塞,返回错误号
      • 解锁
        pthread_mutex_unlock(pthread_mutex_t *restrict mutex);

    死锁

    死锁就是线程自己锁定自己不能向下执行,造成死锁的原因:

    • 自己锁自己
      自己锁了自己两次,线程在第二次上锁位置卡死

    • 线程有两把锁
      有两把锁A1A2,线程1上了A1锁,线程2上了A2锁,线程1想访问A2锁定资源,线程2想访问A1锁定资源,造成互相阻塞

    • 解决死锁

      • 让线程按照一定的顺序访问共享资源
      • 在访问其他锁的时候,需要先将自己的锁解开
      • 使用trylock函数

    读写锁

    • 创建一把锁
      pthread_rwlock_t rwlock;
    • 读写锁的特点
      • 读锁:对内存做读操作
      • 写锁:对内存做写操作
    • 读写锁的特性:
      • 线程A加读锁成功,又来了三个线程,做读操作,可以加锁成功
        • 读共享 -并行处理
      • 线程A加写锁成功,又来了三个线程,做读操作,三个线程阻塞
        • 写独占
      • 线程A加读锁成功,又来了B线程加写锁阻塞,又来了C线程加读锁阻塞
        • 读写不能同时
        • 写的优先级高
    • 读写锁的场景练习
      • 线程A加写锁成功,线程B请求读锁
        • 线程B阻塞
      • 线程A持有读锁,线程B请求写锁
        • 线程B阻塞
      • 线程A拥有毒素哦,线程B请求读锁
        • 线程B加锁成功
      • 线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
        • B阻塞 C阻塞-写的优先级高
        • A解锁,B线程加锁成功,C继续阻塞
        • B解锁,C加读锁成功
      • 线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
        • 线程B阻塞,线程C阻塞
        • 线程A解锁,线程C加锁成功,线程B阻塞
        • 线程C解锁,线程B加锁成功
    • 读写锁适用场景
      • 互斥锁 - 读写串行
      • 读写锁:
        • 读:并行
        • 写:串行
      • 程序中读操作大于写操作的情况
    • 主要操作函数
      • 读写锁初始化函数
        pthread_rwlock_init(pthread_rwlock_t *restrick rwlock,const pthread_relockattr_t *restrick attr);
      • 销毁读写锁
        pthread_rwlock_destroy(pthread_rwlock_t * rwlock);
      • 加读锁
        pthread_rwlock_rdlock(pthread_rwlock_t * rwlock);
      • 尝试加读锁
        pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock);
      • 加写锁
        pthread_rwlock_wrlock(pthread_rwlock_t * rwlock);
      • 尝试加写锁
        pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock);
      • 解锁
        pthread_rwlock_unlock(pthread_rwlock_t * rwlock);

    条件变量

    • 条件变量
      • 不是锁
      • 线程同步需要 条件变量+互斥锁
        • 互斥锁:保护一块共享数据
        • 条件变量:引起阻塞
          • 生产者和消费者模型
    • 粗糙的例子说明
      生产者生产烧饼,消费者购买烧饼,条件变量就是有无烧饼,有烧饼消费者可以买(线程不阻塞),没烧饼消费者等待(线程阻塞),但是烧饼是公共的大家都可以买(为避免冲突烧饼有人买了其他人不能再买(加互斥锁保护))
      - 条件变量的两个动作
      • 条件不满足,阻塞线程
      • 当条件满足,通知阻塞线程开始工作
    • 条件变量类型:pthread_cond_t;
    • 主要函数
      • 初始化一个变量
        pthread_cond_init(pthread_cond_t *restric cond,const pthread_condattr_t*restric attr);
      • 销毁一个变量
        pthread_cond_destroy(pthread_cond_t *cond);
      • 阻塞等待一个条件变量注意第二个参数为互斥锁类型
        pthread_cond_wait(pthread_cond_t *restric cond,pthread_mutex_t*restric mutex);

        • 阻塞线程
        • 将已上锁的mutex解锁
      • 唤醒至少一个阻塞在条件变量上的线程
        pthread_cond_signal(pthread_cond_t *cond);
      • 唤醒全部阻塞在条件变量上的线程
        pthread_cond_broadcast(pthread_cond_t *cond);

    信号量(信号灯)

    • 头文件semaphore.h
    • 粗糙理解
      可以理解成一个车库,车库有N个停车位(信号量个数),当有人占了一个车位就会加锁(信号量减一),当车从车位离开(信号量加一),当车库没有停车位即信号量为0,在想占有停车位资源只能等待(阻塞)
    • 信号量类型
      • sem_t sem
      • 加强版互斥锁
    • 主要函数
      • 初始化信号量
        sem_init(sem_t *sem,int pshared,unsigned int value);

        • sem:信号量
        • pshared0:线程同步 1:进程同步
        • value:最多有几个线程操作共享数据
    • 销毁信号量
      sem_destroy(sem_t *sem);
    • 加锁
      sem_wait(sem_t *sem);
      调用一次相当于对于sem进行了一次减减操作,如果sem值为0,线程将会阻塞
    • 限时尝试加锁
      sem_timedwait(sem_t*sem,xxxxxx);
    • 尝试加锁
      sem_trywait(sem_t*sem)
    • 解锁
      sem_post(sem_t*sem)
      sem进行加加操作

    —·

    展开全文
  • Linux线程(二)

    千次阅读 2020-01-11 20:04:50
    linux线程(二) 一、线程终止: 1.pthread_exit函数: 只需终止某个线程而不需要终止整个进程的三个方法: 从线程函数return,这种方法对主线程不适合,从main函数的return,相当于调用exit函数 线程可以调用...
  • Linux线程(一)

    千次阅读 2020-01-11 16:57:20
    Linux线程(一) 一、线程相关概念? 1.什么是线程: 线程是一个程序内的一个执行路线。更准确的说是线程是“一个进程内的控制序列“ 任意的进程至少有一个执行线程 线程在进程内部执行,本质是在进程地址空间内...
  • Linux线程(三)

    千次阅读 2020-01-11 21:54:42
    Linux线程(三) 一、互斥量 根据前面的分析,得到的结果不是我们想要的原因是–ticket操作不是原子操作,这个共享资源可能并发的切换大其他线程,导致有多个线程同时影响到这个共享资源,所以导致得到的结果不对...
  • Linux线程的调度机制

    千次阅读 2019-01-10 21:32:49
    Linux线程的调度机制  在Linux中,线程是由进程来实现,线程就是轻量级进程( lightweight process ),因此在Linux中,线程的调度是按照进程的调度方式来进行调度的。Linux这样实现的线程的好处的之一是:线程调度...
  • linux线程浅析

    千次阅读 2013-05-11 16:25:07
    关于linux线程  在许多经典的操作系统教科书中, 总是把进程定义为程序的执行实例, 它并不执行什么, 只是维护应用程序所需的各种资源. 而线程则是真正的执行实体. 为了让进程完成一定的工作, 进程必须至少包含...
  • Linux线程调度方式通过线程属性设置

    千次阅读 2018-02-25 22:13:08
    Linux 线程调度方式 在创建线程的时候,我们可以指定所要创建线程的属性,属性中有线程的“调度方式”,线程的调度方式有三种: SCHED_OTHER:分时调度策略 根据nice指来确定线程的运行的顺序(counter+20-nice ...
  • linux线程互斥量pthread_mutex_t使用简介

    万次阅读 多人点赞 2018-06-03 20:30:42
    在多线程应用程序中,当多个线程共享相同的内存时,如同时访问一个变量时,需要确保每个线程看到一致的数据视图,即保证所有线程对数据的修改是一致的。 如下两种情况不存在不一致的问题: 每个线程使用的变量都...
  • linux线程同步的方法

    千次阅读 2018-04-13 15:30:06
    Linux 线程同步的三种方法 线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。 一、互斥锁(mutex) ...
  • 彻底释放Linux线程的资源

    千次阅读 2018-11-13 08:13:40
    彻底释放Linux线程的资源
  • 线程按照其调度者可分为用户级线程和内核级线程两种 a)用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定; b)内核级线程:由内核调度机制实现。  现在大多数操作系统都采用用户级线程和内核...
  • Linux线程(七)

    千次阅读 2020-01-17 12:52:59
    Linux线程(七) 一、单例模式 1.什么是单例模式? 单例模式是一种经典的“设计模式”,保证类在内存中只能有一个对象 2.什么是设计模式? 设计模式是一套被**反复使用的、多数人知晓的、经过分类编目的、代码设计...
  • Linux线程浅析[线程分离] 线程的初始化和销毁 什么是线程的分离 线程分离函数 线程的初始化和销毁回想一下线程的创建pthread_create的时候,第二个参数是pthread_attr_t,那么这个参数类型代表的是什么??attr是特征...
  • Java中有许多线程操作。... 线程状态:执行中,阻塞,就绪一、Linux线程相关系统调用大类作用方法签名大类作用方法签名线程&lt;pthread.h&gt;创建一个新线程,且马上执行int pthread_create...
  • linux线程优先级设置

    千次阅读 2017-03-24 09:09:26
    Linux线程优先级 Linux内核的三种调度策略:  1.SCHED_OTHER 分时调度策略  2.SCHED_FIFO 实时调度策略,先到先服务。一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃  3.SCHED_...
  • Linux线程】线程,进程笔试面试题目汇总 时间:20190905 参考:https://www.cnblogs.com/dreamroute/p/5207813.html   1.进程和线程的介绍 假设cpu是一个工厂,工厂中有很多车间,每个车间又有很多工人。 ...
  • Unix / Linux 线程的实质

    千次阅读 2019-12-12 16:32:17
    Unix / Linux 线程的实质 线程与进程的比较 概述: 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,...
  • Linux线程的内存布局

    千次阅读 2017-08-20 16:40:50
    Linux线程的内存布局在Linux的glibc中,通过pthread结构实现线程。由于线程和主进程是使用同一个虚拟地址空间,所以我们可以通过pmap -X 来比较线程运行前和运行后的虚拟地址空间变化。// gcc main.c -lpthread && ....
  • 文章目录一、Linux线程基本概念二、Linux内核线程实现原理三、创建线程四、线程的优缺点 一、Linux线程基本概念 linux中,线程又叫做轻量级进程(light-weight process LWP),也有PCB,创建线程使用的底层函数和...
  • Linux线程实现机制分析

    千次阅读 2016-01-25 10:03:22
    自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性、效率。本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足,描述了 ...
  • 多线程之linux线程调度策略

    千次阅读 2015-03-21 21:35:40
    linux线程的调度策略分为3个:SCHED_OTHER,SCHED_FIFO,SCHED_RR  讲策略之前,大家需要理解实时与非实时之分。实时就是指操作系统对一些中断等的响应时效性非常高,即使是在内核态的时候,非实时反之。目前像...
  • Linux线程信号

    千次阅读 2010-04-06 12:51:00
    1. 概念按照 POSIX, 异步 (外部) 信号发送到整个进程. 所有线程共享同一个设置, 即通过 sigaction 设置的线程... 由于Linux 线程实现上的独特性, 外部信号始终发送到特定的线程. 2. 例子 #include #include #include
  • Linux线程ID和进程ID

    千次阅读 2014-07-30 15:54:16
    1. Linux线程的线程ID和进程ID Linux内核并不支持真正意义上的线程,Linux线程库是用与普通进程具有同样内核调度视图的轻量级进程来实现线程支持的。这些轻量级进程拥有独立的进程id,在进程调度、信号处理、IO等...
  • Linux线程同步

    千次阅读 2020-03-06 09:20:48
    文章目录一、线程同步的概念二、互斥锁1、初始化锁2、阻塞加锁3、非阻塞加锁4、解锁5、销毁锁(此时锁必需unlock状态,否则返回EBUSY)三、示例程序四、版权声明 一、线程同步的概念 线程同步?怎么同步?一起运行?...
  • Linux线程详解

    万次阅读 多人点赞 2019-06-03 12:06:33
    Linux下由于实现方法导致进程、线程差别不是很大 。 线程控制原语 pthread_self函数 获取线程ID。其作用对应进程中 getpid() 函数。 pthread_t pthread_self(void); 返回值:成功:0; 失败:无! 线程...
  • 【Linux】Linux线程私有数据

    千次阅读 2018-09-10 16:52:08
    线程私有数据 在单线程程序中,函数经常使用全局变量或静态变量,这是不会影响程序的正确性的,但如果线程调用的函数使用全局变量或静态变量,则很可能引起错误。因为这些函数使用的全局变量和静态变量无法为不同的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 52,403
精华内容 20,961
热门标签
关键字:

linux线程

linux 订阅