多线程编程-线程同步方式介绍(二)

来源:互联网 发布:curl windows 64 下载 编辑:程序博客网 时间:2024/06/05 16:08

序言

上一篇文章中介绍了死锁和线程同步的几种方式:互斥锁条件变量读写锁

本文将介绍线程同步的另外几种方式:自旋锁信号量Barriers


1. 自旋锁(Spin Locks)


  • 自旋锁适用的情况:锁被其他线程短期持有(很快会被释放),而且等待该锁的线程不希望在阻塞期间被取消调度,因为这会带来一些开销。

  • 自旋锁与互斥锁的区别

    • 互斥锁通过休眠的方式来阻塞线程。(线程被暂时取消调度,切换至其他可运行的线程)

    • 自旋锁通过忙等的方式来阻塞线程。(busy-waiting, spinning)(线程不会被取消调度,一直处于运行状态)

  • 应用

    • [1] 非抢占式内核中非常有用:比如在非抢占式内核中,中断处理程序是不能被休眠的,它们唯一可以使用的同步原语只有自旋锁。

    • [2] 在用户级并不十分有用:在分时调度类中运行的用户级线程可以被取消调度 - 当它的时间片用完或有一个更高有效级的线程到来的时候。

  • 自旋锁的接口

    • [1] 自旋锁初始化

      int pthread_spin_init(pthread_spinlock_t *lock, int pshared);参数:    lock : 自旋锁变量指针    pshared : 自旋锁的只有一个属性,pshared代表线程进程共享同步属性,它指示了自旋锁如何获得。              pshared = PTHREAD_PROCESS_SHARED : 自旋锁可以被访问该锁的底层内存的线程获得,即使这些线程来自于不同的进程              pshared = PTHREAD_PROCESS_PRIVATE : 只能被本进程内初始化的线程获取。返回值:成功返回0,失败返回错误代码。
    • [2] 自旋锁枷锁

      int pthread_spin_lock(pthread_spinlock_t *lock);int pthread_spin_trylock(pthread_spinlock_t *lock);返回值:成功返回0,失败返回错误代码。要给自旋锁枷锁,可以调用lock和trylock。pthread_spin_lock()在获得锁之前,线程会一直自旋;pthread_spin_trylock()时线程不自旋,如果自旋锁不能立即获得,返回错误EBUSY。
    • [3] 自旋锁解锁
      不论使用lock还是tryklock上锁,都可以调用pthread_spin_unlock()解锁int pthread_spin_unlock(pthread_spinlock_t *lock);返回值:成功返回0,失败返回错误代码。注意:不要调用会在持有自旋锁期间休眠的函数,由于自旋等待可能会浪费大量的CPU资源。


2. 信号量(Semaphore)


  • 信号量:信号量是一种特殊的变量,一个信号量有一个相关联的整数值,该整数值大于等于0,它可以被增加或减小,但对其的关键访问被保证是原子操作。

    • 通过调用sem_wait对相应的整数值减1,即通常所说的P操作。若当前的信号量的值等于0,则该操作会阻塞线程

    • 另外一种通过sem_post对相应值加1,即通常说的V操作

  • 信号量分类:信号量(灯)可作如下分类

    • 二元灯:灯亮/灯灭两种状态,取值1/0。此时与互斥锁相似。

    • 多元灯:大于1的灯数,表示有多个资源,成为多元灯。

  • 信号量适用的情况:信号量既可用于进程间同步,也可以用于线程间同步,这里介绍线程间信号量同步。

  • 信号量函数接口:头文件 semaphore.h

    • [1] 初始化函数

      int sem_init(sem_t *sem, int pshared, unsigned int value); 参数:    sem : 信号量变量指针    pshared : 信号量类型,为0表示为当前进程的局部信号量,为1表示可以在多个进程间共享    value : sem的初始值返回值:调用成功返回0,失败返回-1
    • [2] 信号量值减一

      int sem_wait(sem_t *sem);  功能:以原子操作的方式将信号量的值减1参数:    sem:sem_init()初始化的信号量返回值:成功返回0,失败返回-1
    • [3] 信号量值加一

      int sem_post(sem_t *sem);  功能:以原子操作的方式将信号量的值加1返回值:成功返回0,失败返回-1
    • [4] 信号量清理函数

      int sem_destroy(sem_t *sem);返回值:成功返回0,失败返回-1
  • 信号量和互斥锁的区别:总的来说,信号量的功能覆盖了互斥锁的功能

    • [1] 互斥锁用于互斥,信号量用于同步

      • 这是互斥锁/互斥量和信号量/信号灯的根本区别。
    • [2] 互斥量的值只能为0/1,信号量的值可以大于1

      • 即互斥量只能实现一个资源的互斥访问,但信号量可以实现多个资源的多线程互斥和同步。
      • 当信号量为单值信号量,也可以完成一个资源的互斥访问。
    • [3] 互斥量的加锁和解锁操作必须由同一个线程分别对应使用,而信号量可以由一个线程释放,另一个线程得到。

      互斥和同步的概念:
      互斥:某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。互斥访问是无序访问,无法限制访问者对资源的访问顺序。
      同步:在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源


3. 关卡(Barriers)


  • Barriers:是一种同步机制,可以协调多个并行工作的线程。Barriers使每一个线程等待直到所有合作的线程都到达了同一点,然后再从这一点开始继续执行。

    • pthread_join()函数就是一种Barriers机制,它使一个线程等待另一个线程的结束。比如在主线程中等待所有子线程的结束然后释放资源结束程序。

    • Barriers比pthread_join()更一般化,它允许任意数量的线程等待。

  • Barriers函数接口:头文件 pthread.h

    • [1] 关卡初始化函数

      int pthread_barrier_init( pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned int count);参数:    barrier: 关卡变量指针    attr: 关卡属性,NULL为使用默认属性    count: 指定在所有线程继续运行之前,必须达到该关卡的线程的个数返回值:成功返回0,失败返回出错编号
    • [2] 等待函数:当一个线程率先完成工作到达关卡,该线程可调用等待函数来等待其他所有线程赶上它

      int pthread_barrier_wait( pthread_barrier_t *barrier);返回值:成功返回0或PTHREAD_BARRIER_SERIAL_THREAD,失败返回出错编号       PTHREAD_BARRIER_SERIAL_THREAD: 返回该值的线程为主线程,负责处理所有线程完成工作后的结果说明:在到达关卡的线程数量没有达到count个数时,调用pthread_barrier_wait()函数的线程都会休眠,直到count个线程的最后一个线程调用pthread_barrier_wait()函数时,所有线程都被唤醒。
    • [3] 资源释放函数

      int pthread_barrier_destroy( pthread_barrier_t *barrier);返回值:成功返回0,失败返回错误编号
  • Barriers使用的注意点

    • 初始化barrier对象时,指定的线程count为工作线程个数+1,加1是加上主线程

    • 在程序中线程并不检测调用的pthread_barrier_wait()的返回值是0还是PTHREAD_BARRIER_SERIAL_THREAD,这是因为我们指定使用主线程来合并其他线程的执行结果



Acknowledgements:
http://www.cnblogs.com/nufangrensheng/p/3521654.html
http://blog.csdn.net/ljianhui/article/details/10813469/
http://blog.csdn.net/bbaiggey/article/details/52943267

2017.08.30

原创粉丝点击