Linux多线程编程(二)

  • 2019 年 10 月 6 日
  • 筆記

一番码客 : 挖掘你关心的亮点。 http://efonfighting.imwork.net

4 – 线程的数据处理5 – 线程的同步和互斥:互斥锁信号量条件变量参考

4 – 线程的数据处理

在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。

线程数据和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。也就是说,我们要在线程中使用全局变量,但是这个全局变量在各个线程中是独立的。

例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在A线程里输出的很可能是B线程的出错信息。

要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。

相关的函数和结构:

  • **pthread_key_t key ** : 指向一个键值的指针 pthread_key_t的定义为typedef int pthread_key_t;不论哪个线程调用了
  • **destructor_function ** : 这是一个销毁函数,它是可选的,可以为 NULL,为 NULL 时,则系统调用默认的销毁函数进行相关的数据注销。如果不为空,则在线程退出时(调用 pthread_exit() 函数)时将以 key 锁关联的数据作为参数调用它,以释放分配的缓冲区,或是关闭文件流等。
  • **pthread_setspecific/pthread_getspecific ** : 设置和获取线程变量的值。

5 – 线程的同步和互斥:

互斥锁

互斥锁用来保证一段时间内只有一个线程在执行一段代码。一般使用流程:

  • 定义一个锁(pthread_mutex_t)
  • 初始化锁(pthread_mutex_init)
  • 使用pthread_mutex_lock/pthread_mutex_unlock进行锁定和解锁。

注意:加锁、解锁之间不能return或break,否则会死锁。

信号量

信号量是一种特殊的变量,本质上是一个非负的整数计数器,可以被增加或减少,但系统保证对该变量的访问是原子操作(这能控制多个线程操作同一资源时的顺序问题)。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。

这里需要引入新的头文件semaphore.h

  • sem_init 初始化信号量。该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示这个信号量是当前进程的局部信号量,否则信号量就可以在多个进程之间共享,value为sem的初始值。调用成功时返回0,失败返回-1.
  • sem_post ( sem_t *sem ) 该函数用于以原子操作的方式将信号量的值加1。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞。
  • sem_wait( sem_t *sem ) 被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减1,表明公共资源经使用后减少。
  • sem_destroy 该函数用于清理用完的信号量。

条件变量

互斥锁是用来给资源上锁的,而条件变量是用来等待而不是用来上锁的。

条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。

通常条件变量和互斥锁同时使用。

和条件变量使用有关的几个重要函数:

/*  初始化与销毁:  条件变量采用的数据类型是pthread_cond_t, 在使用之前必须要进行初始化, 这包括两种方式:  - 静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.  - 动态: pthread_cond_init函数, 是释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.  */  int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);  int pthread_cond_destroy(pthread_cond_t *cond);    /*  等待条件:  等待条件函数等待条件变为真, 传递给pthread_cond_wait的互斥量对条件进行保护, 调用者把锁住的互斥量传递给函数. 函数把调用线程放到等待条件的线程列表上, 然后对互斥量解锁, 这两个操作是原子的. 这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化.  当pthread_cond_wait返回时, 互斥量再次被锁住.  */  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_signal(pthread_cond_t *cond);  int pthread_cond_broadcast(pthread_cond_t *cond);  //解除所有线程的阻塞  

参考

  • https://www.ibm.com/developerworks/cn/linux/l-cn-mthreadps/
  • https://cloud.tencent.com/developer/article/1193996
  • https://blog.csdn.net/zsf8701/article/details/7843837
  • Linux 线程调度与优先级: https://www.cnblogs.com/xiaojianliu/p/9689118.html
  • Linux线程同步——条件变量:https://www.cnblogs.com/liangf27/p/9493722.html

免费知识星球: 一番码客-积累交流 微信公众号:一番码客 微信:Efon-fighting 网站:http://efonfighting.imwork.net