1、线程
(1)线程概念
LWP:light weight process 轻量级的进程,本质仍是进程( 在Linux环境下)
进程:独立地址空间,拥有PCB
线程:也有PCB,但没有独立的地址空间( 共享)
区别:在于是否共享地址空间。 独居( 进程) ;合租( 线程) 。
Linux下: 线程:最小的执行单位
进程:最小分配资源单位,可看成是只有一个线程的进程。
(2)线程的特点
1| 轻量级进程( light-weight process) ,也有PCB,创建线程使用的底层函数和进程一样,都是clone
2| 从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的
3| 进程可以蜕变成线程
4| 线程可看做寄存器和栈的集合
5| 在linux下,线程最是小的执行单位;进程是最小的分配资源单位
6| 察看LWP号(线程ID):ps –Lf pid 查看指定线程的lwp号。
(3)线程共享资源
1| 文件描述符表
2| 每种信号的处理方式
3| 当前工作目录
4| 用户ID和组ID
5| 内存地址空间 ( .text/.data/.bss/heap/共享库)
(4)线程非共享资源
1| 线程id
2| 处理器现场和栈指针( 内核栈)
3| 独立的栈空间( 用户空间栈)
4| errno变量
5| 信号屏蔽字
6| 调度优先级
(5)线程优、缺点
优点: 1. 提高程序并发性 2. 开销小 3. 数据通信、共享数据方便
缺点: 1. 库函数,不稳定 2. 调试、编写困难、gdb不支持 3. 对信号支持不好
优点相对突出,缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。
2、线程控制原语
(1)pthread_self函数
1| 作用:获取线程ID。其作用对应进程中 getpid( ) 函数。
2| 函数原型:pthread_t pthread_self( void) ;
3| 返回值:返回值:成功:0; 失败:无!
4| 注意
< 1> 线程ID:pthread_t类型,本质:在Linux下为无符号整数( %lu) ,其他系统中可能是结构体实现
< 2> 线程ID是进程内部,识别标志。( 两个进程间,线程ID允许相同)
(2)pthread_create函数
1| 函数作用:创建一个新线程。 其作用,对应进程中fork( ) 函数。
2| 函数原型:int pthread_create( pthread_t *thread, const pthread_attr_t *attr, void *( *start_routine) ( void *) , void *arg) ;
3| 返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。
4| 参数
< 1> pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t;
< 2> 参数1:传出参数,保存系统为我们分配好的线程ID
< 3> 参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
< 4> 参数3:函数指针,指向线程主函数( 线程体) ,该函数运行结束,则线程结束。
< 5> 参数4:线程主函数执行期间所使用的参数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * thrd_func ( )
{
printf ( "thread id is %lu,pid is %u\n" , pthread_self ( ) , getpid ( ) ) ;
return NULL ;
}
int main ( void )
{
pthread_t tid;
int ret;
printf ( "In main1:thread id is %lu,pid is %u\n" , pthread_self ( ) , getpid ( ) ) ;
ret = pthread_create ( & tid, NULL , thrd_func, NULL ) ;
if ( ret!= 0 )
{
perror ( "pthread_creat error:\n" ) ;
exit ( 1 ) ;
}
sleep ( 1 ) ;
printf ( "In main2:thread id is %lu,pid is %u\n" , pthread_self ( ) , getpid ( ) ) ;
return 0 ;
}
3、循环创建多个线程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * thrd_func ( void * arg)
{
int i;
i = ( int ) arg;
sleep ( i) ;
printf ( "%dthread id is %lu,pid is %u\n" , i+ i, pthread_self ( ) , getpid ( ) ) ;
return NULL ;
}
int main ( void )
{
pthread_t tid;
int ret, i;
for ( i = 0 ; i < 5 ; i++ )
{
ret = pthread_create ( & tid, NULL , thrd_func, ( void * ) i) ;
if ( ret!= 0 )
{
perror ( "pthread_creat error:\n" ) ;
exit ( 1 ) ;
}
}
sleep ( i) ;
printf ( "In main2:thread id is %lu,pid is %u\n" , pthread_self ( ) , getpid ( ) ) ;
return 0 ;
}
4、pthread_exit函数
1| 作用:将单个线程退出
2| 原型:void pthread_exit( void *retval) ;
3| 参数:retval表示线程退出状态,通常传NULL
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * thrd_func ( void * arg)
{
int i;
i = ( int ) arg;
sleep ( i) ;
printf ( "I am %d thread,thread id is %lu,pid is %u\n" , i+ i, pthread_self ( ) , getpid ( ) ) ;
return NULL ;
}
int main ( void )
{
pthread_t tid;
int ret, i;
for ( i = 0 ; i < 5 ; i++ )
{
ret = pthread_create ( & tid, NULL , thrd_func, ( void * ) i) ;
if ( ret!= 0 )
{
perror ( "pthread_creat error:\n" ) ;
exit ( 1 ) ;
}
}
sleep ( i) ;
printf ( "In main2:thread id is %lu,pid is %u\n" , pthread_self ( ) , getpid ( ) ) ;
pthread_exit ( NULL ) ;
}
5、pthread_join函数回收子进程
1| 作用:回收子线程(其作用,对应进程中 waitpid( ) 函数。)
2| 函数原型:int pthread_join( pthread_t thread, void **retval) ;
3| 返回值:成功:0;失败:错误号
4| 参数:thread:线程ID (【注意】:不是指针);retval:存储线程结束状态。
对比记忆:
进程中:main返回值、exit参数--> int;等待子进程结束 wait 函数参数--> int *
线程中:线程主函数返回值、pthread_exit--> void *;等待线程结束 pthread_join 函数参数--> void **
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * thrd_func ( void * arg)
{
pthread_exit ( ( void * ) 10 ) ;
}
int main ( void )
{
pthread_t tid;
int ret;
int * retval;
printf ( "In main 1:thread id = %lu,pid = %u\n" , pthread_self ( ) , getpid ( ) ) ;
ret = pthread_create ( & tid, NULL , thrd_func, NULL ) ;
if ( ret!= 0 )
{
perror ( "pthread_creat error:\n" ) ;
exit ( 1 ) ;
}
pthread_join ( tid, ( void * * ) & retval) ;
printf ( "-------------%d\n" , ( int ) retval) ;
pthread_exit ( NULL ) ;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
typedef struct {
char ch;
int var;
char str[ 64 ] ;
} exit_t;
void * thrd_func ( void * arg)
{
exit_t * retvar = ( exit_t * ) malloc ( sizeof ( exit_t) ) ;
retvar-> ch = 'm' ;
retvar-> var = 200 ;
strcpy ( retvar-> str, "my thread\n" ) ;
pthread_exit ( ( void * ) 10 ) ;
}
int main ( void )
{
pthread_t tid;
int ret;
exit_t * retval;
printf ( "In main 1:thread id = %lu,pid = %u\n" , pthread_self ( ) , getpid ( ) ) ;
ret = pthread_create ( & tid, NULL , thrd_func, ( void * ) i) ;
if ( ret!= 0 )
{
perror ( "pthread_creat error:\n" ) ;
exit ( 1 ) ;
}
pthread_join ( tid, ( void * * ) & retval) ;
printf ( "ch = %c,var = %d,str = %s\n" , retval-> ch, retval-> var, retval-> str) ;
pthread_exit ( NULL ) ;
}
6、循环创建的多个子线程回收
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
imt var = 100 ;
void * tfn ( void * arg)
{
int i;
i = ( int ) arg;
sleep ( i) ;
if ( i == 1 )
{
var = 333 ;
printf ( "I'm %dth pthread,pthread_id = %lu,var = %d\n" , i+ 1 , pthread_self ( ) , var) ;
return ( void * ) var;
}
else if ( i == 3 )
{
var = 777 ;
printf ( "I'm %dth pthread,pthread_id = %lu,var = %d\n" , i+ 1 , pthread_self ( ) , var) ;
pthread_exit ( ( void * ) var) ;
}
else
{
printf ( "I'm %dth pthread,pthread_id = %lu,var = %d\n" , i+ 1 , pthread_self ( ) , var) ;
pthread_exit ( ( void * ) var) ;
}
}
int main ( )
{
pthread_t tid[ 5 ] ;
int i;
int * ret[ 5 ] ;
for ( i= 0 ; i< 5 ; i++ )
{
pthread_create ( & tid, NULL , tfn, ( void * ) i) ;
}
for ( i= 0 ; i< 5 ; i++ )
{
pthread_join ( tid[ i] , ( void * * ) & ret[ i] ) ;
printf ( "==========%d 's ret = %d\n" , i, ( int ) ret[ i] ) ;
}
return 0 ;
}
7、线程分离
(1)pthread_detach函数
1| 作用:实现线程分离
2| 函数原型:int pthread_detach( pthread_t thread) ;
3| 返回值: 成功:0;失败:错误号
8、杀死(取消)线程
(1)pthread_cancel函数
1| 作用:杀死(取消)线程,对应进程中 kill( ) 函数。
2| 函数原型:int pthread_cancel( pthread_t thread) ;
3| 返回值: 成功:0;失败:错误号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void * tfn1 ( void * arg)
{
printf ( "thread 1 retruning\n" ) ;
return ( void * ) 111 ;
}
void * tfn2 ( void * arg)
{
printf ( "thread 2 retruning\n" ) ;
return ( void * ) 222 ;
}
void * tfn3 ( void * arg)
{
while ( 1 )
{
pthread_testcancel ( ) ;
}
}
int main ( )
{
pthread_t tid;
void * tret = NULL ;
pthread_create ( & tid, NULL , tfn1, NULL ) ;
pthread_join ( tid, & tret) ;
printf ( "thread 1 exit code = %d\n\n" , ( int ) tret) ;
pthread_create ( & tid, NULL , tfn2, NULL ) ;
pthread_join ( tid, & tret) ;
printf ( "thread 2 exit code = %d\n\n" , ( int ) tret) ;
pthread_create ( & tid, NULL , tfn3, NULL ) ;
sleep ( 3 ) ;
pthread_cancel ( tid) ;
pthread_join ( tid, & tret) ;
printf ( "thread 3 exit code = %d\n\n" , ( int ) tret) ;
return 0 ;
}
(2)控制原语对比
进程 线程
fork pthread_create
exit pthread_exit
wait pthread_join
kill pthread_cancel
getpid pthread_self 命名空间
9、线程属性
(1)线程属性初始化
1| 注意:应先初始化线程属性,再pthread_create创建线程
2| 初始化线程属性
int pthread_attr_init( pthread_attr_t *attr) ; 成功:0;失败:错误号
3| 销毁线程属性所占用的资源
int pthread_attr_destroy( pthread_attr_t *attr) ; 成功:0;失败:错误号
(2)线程的分离状态
1| 设置线程属性,分离or非分离
int pthread_attr_setdetachstate( pthread_attr_t *attr, int detachstate) ;
2| 获取程属性,分离or非分离
int pthread_attr_getdetachstate( pthread_attr_t *attr, int *detachstate) ;
3| 参数:
attr:已初始化的线程属性
detachstate: PTHREAD_CREATE_DETACHED(分离线程)、PTHREAD _CREATE_JOINABLE(非分离线程)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void * thrd_func ( void * arg)
{
pthread_exit ( ( void * ) 77 ) ;
}
int main ( )
{
pthread_t tid;
int ret;
pthread_attr_t attr;
ret = pthread_attr_init ( & attr) ;
if ( ret!= 0 )
{
fprintf ( stderr , "pthread_init error:%s\n" , strerror ( ret) ) ;
exit ( 1 ) ;
}
pthread_attr_setdetachstate ( & attr, PTHREAD_CREATE_DETACHED) ;
ret = pthread_create ( & tid, & attr, thrd_func, NULL ) ;
if ( ret!= 0 )
{
fprintf ( stderr , "pthread_init error:%s\n" , strerror ( ret) ) ;
exit ( 1 ) ;
}
ret = pthread_join ( tid, NULL ) ;
{
fprintf ( stderr , "pthread_init error:%s\n" , strerror ( ret) ) ;
exit ( 1 ) ;
}
printf ( "--------------------join ret = %d\n" , ret) ;
pthread_exit ( ( void * ) 1 ) ;
}
(3)线程的栈地址
1| 当进程栈地址空间不够用时,指定新建线程使用由malloc分配的空间作为自己的栈空间。通过pthread_attr_setstack和pthread_attr_getstack两个函数分别设置和获取线程的栈地址。
2| int pthread_attr_setstack( pthread_attr_t *attr, void *stackaddr, size_t stacksize) ; 成功:0;失败:错误号
3| int pthread_attr_getstack( pthread_attr_t *attr, void **stackaddr, size_t *stacksize) ; 成功:0;失败:错误号
4| 参数: attr:指向一个线程属性的指针
stackaddr:返回获取的栈地址
stacksize:返回获取的栈大小
(4)线程的栈大小
1| 当系统中有很多线程时,可能需要减小每个线程栈的默认大小,防止进程的地址空间不够用,当线程调用的函数会分配很大的局部变量或者函数调用层次很深时,可能需要增大线程栈的默认大小。
2| 函数pthread_attr_getstacksize和 pthread_attr_setstacksize提供设置。
3| int pthread_attr_setstacksize( pthread_attr_t *attr, size_t stacksize) ; 成功:0;失败:错误号
4| int pthread_attr_getstacksize( pthread_attr_t *attr, size_t *stacksize) ; 成功:0;失败:错误号
5| 参数: attr:指向一个线程属性的指针
stacksize:返回线程的堆栈大小
#include <pthread.h>
#define SIZE 0x100000
void * th_fun ( void * arg)
{
while ( 1 )
sleep ( 1 ) ;
}
int main ( void )
{
pthread_t tid;
int err, detachstate, i = 1 ;
pthread_attr_t attr;
size_t stacksize;
void * stackaddr;
pthread_attr_init ( & attr) ;
pthread_attr_getstack ( & attr, & stackaddr, & stacksize) ;
pthread_attr_getdetachstate ( & attr, & detachstate) ;
if ( detachstate == PTHREAD_CREATE_DETACHED)
printf ( "thread detached\n" ) ;
else if ( detachstate == PTHREAD_CREATE_JOINABLE)
printf ( "thread join\n" ) ;
else
printf ( "thread unknown\n" ) ;
pthread_attr_setdetachstate ( & attr, PTHREAD_CREATE_DETACHED) ;
while ( 1 ) {
stackaddr = malloc ( SIZE) ;
if ( stackaddr == NULL ) {
perror ( "malloc" ) ;
exit ( 1 ) ;
}
stacksize = SIZE;
pthread_attr_setstack ( & attr, stackaddr, stacksize) ;
err = pthread_create ( & tid, & attr, th_fun, NULL ) ;
if ( err != 0 ) {
printf ( "%s\n" , strerror ( err) ) ;
exit ( 1 ) ;
}
printf ( "%d\n" , i++ ) ;
}
pthread_attr_destroy ( & attr) ;
return 0 ;
}
10、线程使用注意事项
1| 主线程退出其他线程不退出,主线程应调用pthread_exit
2| 避免僵尸线程
pthread_join
pthread_detach
pthread_create指定分离属性
被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
3| malloc和mmap申请的内存可以被其他线程释放
4| 应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
5| 信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制
11、线程同步
(1)概念
线程同步,指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。
(2)注意点
所有“多个控制流,共同操作一个共享资源”的情况,都需要同步。
12、互斥量mutex
(1)概念
1| Linux中提供一把互斥锁mutex(也称之为互斥量)。
2| 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
3| 资源还是共享的,线程间也还是竞争的,
4| 但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
(2)主要应用函数
1| pthread_mutex_init函数
2| pthread_mutex_destroy函数
3| pthread_mutex_lock函数
4| pthread_mutex_trylock函数
5| pthread_mutex_unlock函数
6| 以上5个函数的返回值都是:成功返回0, 失败返回错误号。
pthread_mutex_t 类型,其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。
pthread_mutex_t mutex; 变量mutex只有两种取值1、0。
13、函数详述
(1)pthread_mutex_init函数
1| 作用:初始化一个互斥锁( 互斥量) ---> 初值可看作1
2| 函数原型:int pthread_mutex_init( pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr) ;
3| 参数说明:
< 1> 参1:传出参数,调用时应传 & mutex
< 2> 参2:互斥量属性。是一个传入参数,通常传NULL,选用默认属性( 线程间共享) 。 参APUE.12.4同步属性
静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
动态初始化:局部变量应采用动态初始化。e.g. pthread_mutex_init( & mutex, NULL)
(2)pthread_mutex_destroy函数
1| 作用:销毁一个互斥锁
2| 函数原型:int pthread_mutex_destroy( pthread_mutex_t *mutex) ;
(3)pthread_mutex_lock函数
1| 作用:加锁。可理解为将mutex--(或-1)
2| 函数原型:int pthread_mutex_lock( pthread_mutex_t *mutex) ;
(4)pthread_mutex_unlock函数
1| 作用:解锁。可理解为将mutex ++(或+1)
2| 函数原型:int pthread_mutex_unlock( pthread_mutex_t *mutex) ;
(5)pthread_mutex_trylock函数
1| 作用:尝试加锁
2| 函数原型:int pthread_mutex_trylock( pthread_mutex_t *mutex) ;
14、读写锁
(1)介绍
1| 读锁、写锁并行阻塞,写锁优先级高
2| 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。
3| 读写锁非常适合于对数据结构读的次数远大于写的情况。
(2)相关函数
pthread_rwlock_init函数
pthread_rwlock_destroy函数
pthread_rwlock_rdlock函数
pthread_rwlock_wrlock函数
pthread_rwlock_tryrdlock函数
pthread_rwlock_trywrlock函数
pthread_rwlock_unlock函数
以上7 个函数的返回值都是:成功返回0, 失败直接返回错误号。
pthread_rwlock_t类型 用于定义一个读写锁变量。
pthread_rwlock_t rwlock;
(3)函数详述
1| pthread_rwlock_init函数
功能: 初始化一把读写锁
函数原型:int pthread_rwlock_init( pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr) ;
参2:attr表读写锁属性,通常使用默认属性,传NULL即可。
2| pthread_rwlock_destroy函数
功能: 销毁一把读写锁
函数原型:int pthread_rwlock_destroy( pthread_rwlock_t *rwlock) ;
3| pthread_rwlock_rdlock函数
功能: 以读方式请求读写锁。(常简称为:请求读锁)
函数原型:int pthread_rwlock_rdlock( pthread_rwlock_t *rwlock) ;
4| pthread_rwlock_wrlock函数
功能: 以写方式请求读写锁。(常简称为:请求写锁)
函数原型:int pthread_rwlock_wrlock( pthread_rwlock_t *rwlock) ;
5| pthread_rwlock_unlock函数
功能: 解锁
函数原型:int pthread_rwlock_unlock( pthread_rwlock_t *rwlock) ;
6| pthread_rwlock_tryrdlock函数
功能: 非阻塞以读方式请求读写锁(非阻塞请求读锁)
函数原型:int pthread_rwlock_tryrdlock( pthread_rwlock_t *rwlock) ;
7| pthread_rwlock_trywrlock函数
功能: 非阻塞以写方式请求读写锁(非阻塞请求写锁)
函数原型:int pthread_rwlock_trywrlock( pthread_rwlock_t *rwlock) ;
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
int counter;
pthread_rwlock_t rwlock;
void * th_write ( void * arg)
{
int t, i = ( int ) arg;
while ( 1 ) {
pthread_rwlock_wrlock ( & rwlock) ;
t = counter;
usleep ( 1000 ) ;
printf ( "=======write %d: %lu: counter=%d ++counter=%d\n" , i, pthread_self ( ) , t, ++ counter) ;
pthread_rwlock_unlock ( & rwlock) ;
usleep ( 10000 ) ;
}
return NULL ;
}
void * th_read ( void * arg)
{
int i = ( int ) arg;
while ( 1 ) {
pthread_rwlock_rdlock ( & rwlock) ;
printf ( "----------------------------read %d: %lu: %d\n" , i, pthread_self ( ) , counter) ;
pthread_rwlock_unlock ( & rwlock) ;
usleep ( 2000 ) ;
}
return NULL ;
}
int main ( void )
{
int i;
pthread_t tid[ 8 ] ;
pthread_rwlock_init ( & rwlock, NULL ) ;
for ( i = 0 ; i < 3 ; i++ )
pthread_create ( & tid[ i] , NULL , th_write, ( void * ) i) ;
for ( i = 0 ; i < 5 ; i++ )
pthread_create ( & tid[ i+ 3 ] , NULL , th_read, ( void * ) i) ;
for ( i = 0 ; i < 8 ; i++ )
pthread_join ( tid[ i] , NULL ) ;
pthread_rwlock_destroy ( & rwlock) ;
return 0 ;
}
15、条件变量
(1)概述
条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。
(2)相关函数
pthread_cond_init函数
pthread_cond_destroy函数
pthread_cond_wait函数
pthread_cond_timedwait函数
pthread_cond_signal函数
pthread_cond_broadcast函数
以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。
pthread_cond_t类型 用于定义条件变量
pthread_cond_t cond;
(3)函数详述
1| pthread_cond_init函数
功能: 初始化一个条件变量
函数原型:int pthread_cond_init( pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr) ;
参2:attr表条件变量属性,通常为默认值,传NULL即可
也可以使用静态初始化的方法,初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2| pthread_cond_destroy函数
功能: 销毁一个条件变量
函数原型:int pthread_cond_destroy( pthread_cond_t *cond) ;
3| pthread_cond_wait函数
功能: 阻塞等待一个条件变量
函数原型:int pthread_cond_wait( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) ;
函数作用:
< 1> 阻塞等待条件变量cond(参1)满足
< 2> 释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock( & mutex) ;
1.2.两步为一个原子操作。
< 3> 当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock( & mutex) ;
4| pthread_cond_signal函数
功能: 唤醒至少一个阻塞在条件变量上的线程
函数原型:int pthread_cond_signal( pthread_cond_t *cond) ;
5| pthread_cond_broadcast函数
功能: 唤醒全部阻塞在条件变量上的线程
函数原型:int pthread_cond_broadcast( pthread_cond_t *cond) ;
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct msg {
struct msg * next;
int num;
} ;
struct msg * head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void * consumer ( void * p)
{
struct msg * mp;
for ( ; ; ) {
pthread_mutex_lock ( & lock) ;
while ( head == NULL ) {
pthread_cond_wait ( & has_product, & lock) ;
}
mp = head;
head = mp-> next;
pthread_mutex_unlock ( & lock) ;
printf ( "-Consume ---%d\n" , mp-> num) ;
free ( mp) ;
sleep ( rand ( ) % 5 ) ;
}
}
void * producer ( void * p)
{
struct msg * mp;
while ( 1 ) {
mp = malloc ( sizeof ( struct msg) ) ;
mp-> num = rand ( ) % 1000 + 1 ;
printf ( "-Produce ---%d\n" , mp-> num) ;
pthread_mutex_lock ( & lock) ;
mp-> next = head;
head = mp;
pthread_mutex_unlock ( & lock) ;
pthread_cond_signal ( & has_product) ;
sleep ( rand ( ) % 5 ) ;
}
}
int main ( int argc, char * argv[ ] )
{
pthread_t pid, cid;
srand ( time ( NULL ) ) ;
pthread_create ( & pid, NULL , producer, NULL ) ;
pthread_create ( & cid, NULL , consumer, NULL ) ;
pthread_join ( pid, NULL ) ;
pthread_join ( cid, NULL ) ;
return 0 ;
}
(4)条件变量的优点
1| 相较于mutex而言,条件变量可以减少竞争。
2| 如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
(5)生产者消费者条件变量模型
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct msg {
struct msg * next;
int num;
} ;
struct msg * head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void * consumer ( void * p)
{
struct msg * mp;
for ( ; ; ) {
pthread_mutex_lock ( & lock) ;
while ( head == NULL ) {
pthread_cond_wait ( & has_product, & lock) ;
}
mp = head;
head = mp-> next;
pthread_mutex_unlock ( & lock) ;
printf ( "-Consume ---%d\n" , mp-> num) ;
free ( mp) ;
sleep ( rand ( ) % 5 ) ;
}
}
void * producer ( void * p)
{
struct msg * mp;
while ( 1 ) {
mp = malloc ( sizeof ( struct msg) ) ;
mp-> num = rand ( ) % 1000 + 1 ;
printf ( "-Produce ---%d\n" , mp-> num) ;
pthread_mutex_lock ( & lock) ;
mp-> next = head;
head = mp;
pthread_mutex_unlock ( & lock) ;
pthread_cond_signal ( & has_product) ;
sleep ( rand ( ) % 5 ) ;
}
}
int main ( int argc, char * argv[ ] )
{
pthread_t pid, cid;
srand ( time ( NULL ) ) ;
pthread_create ( & pid, NULL , producer, NULL ) ;
pthread_create ( & cid, NULL , consumer, NULL ) ;
pthread_join ( pid, NULL ) ;
pthread_join ( cid, NULL ) ;
return 0 ;
}
16、信号量(进化版的互斥锁(1 --> N))
(1)主要应用函数
sem_init函数
sem_destroy函数
sem_wait函数
sem_trywait函数
sem_timedwait函数
sem_post函数
1| 以上6 个函数的返回值都是:成功返回0, 失败返回-1,同时设置errno。( 注意,它们没有pthread前缀)
2| sem_t类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。
3| sem_t sem; 规定信号量sem不能 < 0。头文件 < semaphore.h>
(2)信号量基本操作
sem_wait: 1. 信号量大于0,则信号量-- (类比pthread_mutex_lock)
| 2. 信号量等于0,造成线程阻塞
对应
|
sem_post: 将信号量++,同时唤醒阻塞在信号量上的线程 (类比pthread_mutex_unlock)
但,由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数来实现,而不能直接++、--符号。
信号量的初值,决定了占用信号量的线程的个数。
1| sem_init函数
作用: 初始化一个信号量
函数原型:int sem_init( sem_t *sem, int pshared, unsigned int value) ;
参1:sem信号量
参2:pshared取0用于线程间;取非0(一般为1)用于进程间
参3:value指定信号量初值
2| sem_destroy函数
作用: 销毁一个信号量
函数原型:int sem_destroy( sem_t *sem) ;
3| sem_wait函数
作用: 给信号量加锁 --
函数原型:int sem_wait( sem_t *sem) ;
4| sem_post函数
作用: 给信号量解锁 ++
函数原型:int sem_post( sem_t *sem) ;
5| sem_trywait函数
作用: 尝试对信号量加锁 -- ( 与sem_wait的区别类比lock和trylock)
函数原型:int sem_trywait( sem_t *sem) ;
6| sem_timedwait函数
作用: 限时尝试对信号量加锁 --
函数原型:int sem_timedwait( sem_t *sem, const struct timespec *abs_timeout) ;
参2:abs_timeout采用的是绝对时间。
定时1秒:
time_t cur = time( NULL) ; 获取当前时间。
struct timespec t; 定义timespec 结构体变量t
t.tv_sec = cur+1; 定时1秒
t.tv_nsec = t.tv_sec +100;
sem_timedwait( & sem, & t) ; 传参
17、进程间同步
(1)互斥量mutex
进程间也可以使用互斥锁,来达到同步的目的。但应在pthread_mutex_init初始化之前,修改其属性为进程间共享。mutex的属性修改函数主要有以下几个。
(2)主要应用函数
1| pthread_mutexattr_t mattr 类型: 用于定义mutex锁的【属性】
2| pthread_mutexattr_init函数: 初始化一个mutex属性对象
int pthread_mutexattr_init( pthread_mutexattr_t *attr) ;
3| pthread_mutexattr_destroy函数: 销毁mutex属性对象 ( 而非销毁锁)
int pthread_mutexattr_destroy( pthread_mutexattr_t *attr) ;
4| pthread_mutexattr_setpshared函数: 修改mutex属性。
int pthread_mutexattr_setpshared( pthread_mutexattr_t *attr, int pshared) ;
参2:pshared取值:
< 1> 线程锁:PTHREAD_PROCESS_PRIVATE ( mutex的默认属性即为线程锁,进程间私有)
< 2> 进程锁:PTHREAD_PROCESS_SHARED
#include <fcntl.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/wait.h>
struct mt {
int num;
pthread_mutex_t mutex;
pthread_mutexattr_t mutexattr;
} ;
int main ( void )
{
int fd, i;
struct mt * mm;
pid_t pid;
fd = open ( "mt_test" , O_CREAT | O_RDWR, 0777 ) ;
ftruncate ( fd, sizeof ( * mm) ) ;
mm = mmap ( NULL , sizeof ( * mm) , PROT_READ| PROT_WRITE, MAP_SHARED, fd, 0 ) ;
close ( fd) ;
unlink ( "mt_test" ) ;
memset ( mm, 0 , sizeof ( * mm) ) ;
pthread_mutexattr_init ( & mm-> mutexattr) ;
pthread_mutexattr_setpshared ( & mm-> mutexattr, PTHREAD_PROCESS_SHARED) ;
pthread_mutex_init ( & mm-> mutex, & mm-> mutexattr) ;
pid = fork ( ) ;
if ( pid == 0 ) {
for ( i = 0 ; i < 10 ; i++ ) {
pthread_mutex_lock ( & mm-> mutex) ;
( mm-> num) ++ ;
printf ( "-child----num++ %d\n" , mm-> num) ;
pthread_mutex_unlock ( & mm-> mutex) ;
sleep ( 1 ) ;
}
} else if ( pid > 0 ) {
for ( i = 0 ; i < 10 ; i++ ) {
sleep ( 1 ) ;
pthread_mutex_lock ( & mm-> mutex) ;
mm-> num + = 2 ;
printf ( "-parent---num+=2 %d\n" , mm-> num) ;
pthread_mutex_unlock ( & mm-> mutex) ;
}
wait ( NULL ) ;
}
pthread_mutexattr_destroy ( & mm-> mutexattr) ;
pthread_mutex_destroy ( & mm-> mutex) ;
munmap ( mm, sizeof ( * mm) ) ;
return 0 ;
}
(3)文件锁
1| fcntl函数
< 1> 作用:获取、设置文件访问控制属性。
< 2> 函数原型:int fcntl( int fd, int cmd, .. . /* arg */ ) ;
< 3> 参数说明:
参2:
F_SETLK ( struct flock *) 设置文件锁(trylock)
F_SETLKW ( struct flock *) 设置文件锁(lock)W --> wait
F_GETLK ( struct flock *) 获取文件锁
参3:
struct flock {
.. .
short l_type; 锁的类型:F_RDLCK 、F_WRLCK 、F_UNLCK
short l_whence; 偏移位置:SEEK_SET、SEEK_CUR、SEEK_END
off_t l_start; 起始偏移:1000
off_t l_len; 长度:0表示整个文件加锁
pid_t l_pid; 持有该锁的进程ID:( F_GETLK only)
.. .
} ;
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
void sys_err ( char * str)
{
perror ( str) ; exit ( 1 ) ;
}
int main ( int argc, char * argv[ ] )
{
int fd;
struct flock f_lock;
if ( argc < 2 ) {
printf ( "./a.out filename\n" ) ; exit ( 1 ) ;
}
if ( ( fd = open ( argv[ 1 ] , O_RDWR) ) < 0 )
sys_err ( "open" ) ;
f_lock. l_type = F_RDLCK;
f_lock. l_whence = SEEK_SET ;
f_lock. l_start = 0 ;
f_lock. l_len = 0 ;
fcntl ( fd, F_SETLKW, & f_lock) ;
printf ( "get flock\n" ) ;
sleep ( 10 ) ;
f_lock. l_type = F_UNLCK;
fcntl ( fd, F_SETLKW, & f_lock) ;
printf ( "un flock\n" ) ;
close ( fd) ; return 0 ;
}
(4)注意
多线程间共享文件描述符,而给文件加锁,是通过修改文件描述符所指向的文件结构体中的成员变量来实现的。因此,多线程中无法使用文件锁。