Linux学习总结(六)

来源:互联网 发布:windows live安装包 编辑:程序博客网 时间:2024/05/16 09:44

Linux学习总结(六)

linux多线程编程

在linux下,线程也被称为轻量级进程

创建线程

函数pthread_create()用来创建一个新的线程

int pthread_creat(pthread_t *_restrict _newhread, _const pthread_attr_t *_restrict_attr, void*(*_start_routine)(void *), void *_restrict_arg)

第一个参数用来存储线程ID,参数为指向线程ID的指针
第二个参数用来设置线程属性,主要设置与栈祥光的属性
第三个参数是线程运行的代码起始地址,即在此线程中运行那些代码
第四个参数是运行函数的参数地址,如果需要传入多个参数,则需要使用一个包含这些参数的结构体地址
此函数如果执行成功,将返回0,如果失败,返回非0值

线程退出和等待

新创建的线程从执行用户定义的函数处开始执行,直到出现下列情况时退出
调用pthread_exit函数退出
其他线程调用pthread_cancel函数取消该线程,且线程可被取消
创建线程的进程退出或整个函数结束
其中一个执行了exec类函数执行新的代码,替换当前进程所有的地址空间
当前线程代码执行完毕

void pthread_exit(void *_retval);

使用pthread_exit库函数调用可以结束一个线程,其结束方式与进程调用exit()函数类似,此函数只有一个参数,即线程退出状态。

等待线程

一般情况下,为了有效同步子线程,在主线程中都将等待子线程结束,显示的等待某线程结束可以调用pthread_join()函数,其类似于进程的wait()函数

int pthread_join(pthread_t _th, void **_thread_return);

此函数将阻塞调用当前线程的线程,直到此线程退出,当函数返回时,处于被等待状态的线程资源被收回
第一个参数为被等待的线程ID,此线程必须同调用它的线程相联系,而不能是独立的线程,如果需要设置某个线程为独立线程,则可以调用pthread_detach()函数

int pthread_detach(pthread_t _th)

此函数如果执行成功则返回0,独立线程终止时,系统将自动回收他的资源,如果执行失败,将返回非零值
第二个参数为一个用户定义的指针,指向一个保存等待线程的完整退出状态的静态区域,它可以用来存储被等待线程的返回值
线程退出前操作

pthread_cleanup_push()/pthread_cleanup_pop()

函数用于自动释放资源,两函数采用先入后出的栈结构来管理

void pthread_cleanup_pus(void (*routine)(void *), void  *arg)void pthread_cleanup_pop(int execute)

参数void routine(void *arg)在调用pthread_cleanup_push()时压入清理函数栈,多次调用pthread_cleanup_push()将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出(arg为前面函数的参数)
参数execute表示执行到ptherad_cleanup_pop()时,是否在弹出清理函数的同时执行函数,为0表示不执行,非0为执行。这个参数并不影响异常退出时清理函数的执行。

取消线程

发起取消操作
取消线程是指取消一个正在执行线程的操作。一个线程能够被取消并终止执行需要满足以下条件
* (1)线程是否可以被其他取消,默认可以被取消
* (2)线程处于取消点才能取消,也就是说,即使该线程被设置为可以取消状态,另一个线程发起取消操作,该线程不一定马上终止,只能在可取消点才终止执行,可以设置线程为立即取消或只能在取消点被取消
函数pthread_cancel()用来向某线程发送取消操作

int ptherad_cancel(pthread_t _cancelthread)pthread_cancel()

函数请求取消执行线程,仅当目标线程可取消状态为PTHREAD_CANCEL_ENABLE时,才可进行取消
执行取消操作时,将调用线程的取消清理处理程序(pthread_cleanup_push函数)。调用取消清理处理程序的顺序与安装这些处理程序的顺序相反,而pthread_cannel()的调用者不会等待目标线程操作完成
设置可取消状态
pthread_setcancelstate()和pthread_setcanceltype()可用来设置和查询当前的可取消性状态或类型。

int pthread_setcancelstate(int _state, int *_oldstate);

此函数有两个参数,state为要设置的新状态值,oldstate储存原来的状态,state的合法值如下
PTHREA_CANCEL_DISABLE,则针对目标线程的取消请求将处于未决状态。除非该线程修改自己的状态,否则不会被取消
PTHREAD_CANCEL_ENABLE,则针对目标线程的取消请求被传递。在创建这个线程时,默认状态为PTHREAD_CANCLE_ENABLE
设置取消类型
pthread_setcanceltype()函数用来设置取消类型,即允许被取消的线程在接收到取消操作后是立即中止还是在取消点终止

int pthread_setcanceltype(int _type, int *_oldtype)

此函数有两个参数,type为调用线程新的可取消性,oldtype存储原来的类型,type的合法值如下
PTHREAD_CANCEL_ASYNCHRONOUS,可随时执行新的或未决的取消请求
PTHRAD_cANCEL_DEFERRED,在目标线程到达一个取消点之前,取消请求将一直处于未决状态,未决状态的意思即请求已经发出,但对方未处理状态
在创建某个线程时,其可取消性类型设置为PTHREAD_CANCEL_DEFERRED
如果禁用了线程的可取消性状态,则该线程的可取消性类型的设置不会立即生效。所有的取消请求都保留位未决状态,但是,一旦重新启用了可去小型,则新的类型将会生效
成功完成后,两函数都将返回0,否则,将返回错误编号以指明错误(不设置errno变量)。

线程与私有数据

线程私有数据TSD(Thread-specific Data)

创建、注销线程私有数据

函数pthread_key_create()用来创建线程私有数据

int pthread_key_create(pthread_key_t *key, void (*destr_function)(void *))

该函数从TSD池中分配一项,将其地址值赋给key供以后访问使用,如果第二个参数不为空,在现场退出时将以key所关联的数据为参数调用其指向的资源释放函数,以释放分配的缓冲区
不论哪个现场调用pthread_key_create(),所创建的key都是所有线程可访问的,但各个线程可根据自己的需要往key只能填入不同的值,相当于提供了一个同名而不同值的全局变量
注销一个TSD采用如下API:

intpthread_key_delete(pthread_key_t key)

这个函数并不检查当前是否有线程正使用该TSD,也不会调用清理函数,而只是将TSD释放以供下一次调用pthread_key_cread()使用。在linux threads中,它还会将与之相关的线程数据设为NULL

读取线程私有数据

TSD的读写都通过专门的Posix Thread函数进行,其API如下

int pthread_setspecifie(pthread_key_t key, const void *pointer)void *ptherad_getspecific(pthread_key_t key)

函数pthread_setspecific()将pointer的值与key相关联,pthread_getspecifid()函数将与key相关联的数据读出来。数据类型都设置为void*,因此可以向任何类型的数据

线程同步机制

互斥锁通信机制

互斥锁以排他的方式防止共享数据被并发访问,互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁在逻辑上绑定(即要申请该资源必须要先获取锁),对该共享资源的访问操作如下
* (1)在访问该资源前,首先申请该互斥锁,如果该互斥处于开锁状态,则申请到该锁对象,并立即占有该锁,一以防止其他线程访问该资源,如果该互斥锁处于锁定状态,默认阻塞当前进程。
* (2)只有锁定该互斥锁的进程才能释放该互斥锁。其他线程试图释放操作无效
初始化和销毁互斥锁
在使用互斥锁前,需要定义该互斥锁(全局变量)
pthrad_mutex_t lock;
初始化和销毁互斥锁的函数分别为pthread_mutes_init和pthread_mutex_destroy。互斥锁初始化函数声明如下

int pthread_mutex_int(pthread_mutex_t *_mutex, _const pthread_mutexattr_t *_mutexattr)

第一个参数mutex是指向要初始化的互斥锁的指针
第二个参数mutexattr是指向属性对象的指针,该属性对象定义要初始化的互斥锁的属性。如果该指针为NULL,则使用默认的属性此外,还可以使用宏PTHREAD_MUTEX_INITIALIZER初始化静态分配的互斥锁。
销毁互斥锁函数

int pthread_mutex_destroy(pthread_mutex *_mutex)

成功完成后,pthrad_mutex_init()和pthread_mutex_destroy()将返回0.否则,将返回错误编号知名错误
申请互斥锁
如果一个线程要占用一共享资源,其必须先申请对应的互斥锁。pthread_mutex_lock()函数以阻塞方式申请互斥锁。

int pthread_mutex_lock(pthread_mutex_t *_mutex)pthread_mutex_trylock()函数以非阻塞方式申请互斥锁int pthread_mutex_trylock(pthread_mutex_t *mutex)两函数如果成功完成,将返回0,

释放互斥锁
pthread_mutex_unlock()函数用来释放互斥锁
intpthread_mutex_unlock(pthread_mutex_t *_mutex)
其参数mutex为指向要解锁的互斥锁的指针,释放操作只能由占有该互斥锁的线程完成。如果成功完成,phtread_mutex_unlock()返回0

条件变量

条件变量不能单独使用,必须配合互斥锁一起实现对资源的互斥访问
初始化、销毁条件变量
在使用条件变量前,需要定义该条件变量,定以条件变量对象的代码如下

pthread_cond_t condition;

pthread_cond_init()和pthread_cond_destroy()函数分别用来初始化和销毁条件变量
pthread_cond_init()函数可使用属性attr来初始化条件变量cond,如果attr为NULL,则使用默认的属性对象来初始化条件变量属性

int pthread_cond_init(pthread_cond_t *_restrict_cond, _const pthread_condattr_t *_restrict _cond_attr)

第一个参数cond是指向要初始化或损坏的条件变量的指针
第二个参数cond_atrr是指向属性对象的指针,该属性对象定义要初始化的条件变量的特性,如果该指针为NULL,则使用默认的属性
函数pthread_cond_destroy()用来销毁条件变量。

int pthread_cond_destroy(pthread_cond_t *_cond)

两函数成功完成后,将返回0
通知等待条件变量的线程
pthread_cond_signal()函数用来通知等待条件变量的第一个线程。pthread_cond_broadcast()函数用来通知等待条件变量的所有线程

int pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *_cond);

其参数cond是指向要通知或广播的条件变量的指针
两函数成功完成后,将返回0

等待条件变量

pthread_cond_wait()函数用来阻塞等待某个条件变量

int pthread_cond_wait(pthread_cond_t *_restrict_cond, pthread_mutex_t *_restrict_mutex);

第一个参数cond是指向要等待的条件变量的指针
第二个参数mutex是指向与条件变量cond关联的互斥锁的指针
pthread_cond_timewait()函数将在指定的时间范围内等待条件变量,其函数声明如下

int pthread_cond_timewait(pthread_cond_t *_restrict_cond, pthread_mutex_t *_restrict_mutex, _const struct timespec *_restrict_abstime);

第一个参数cond是指向要等待的条件变量的指针
第二个参数mutex是指向与条件变量cond关联的互斥锁的指针
第三个参数abstime是等待过期时的绝对时间,如果在此时间范围内取到该条件变量函数将返回,该时间为从1970-1-1:0:0:0以来的秒数,即为一个绝对时间,该数据结构为

struct timespec{    long ts_sec;    long ts_nsec;};

以上两个函数都包含一个互斥锁,如果某线程因等待条件变量进入等待状态时,将隐含释放其申请的互斥锁,同样,在返回时,隐含申请到该互斥锁对象操作
两函数成功执行时,返回0

读写锁通信机制

读写锁基本原理
posix线程提供了读写锁机制

  • (1)如果当前线程读数据,则允许其他线程执行读操作,但不允许写操作
  • (2)如果当前线程写数据,则其他线程的读、写操作均不允许

因此,读写锁分为了读锁和写锁

  • (1)如果某线程申请了读锁,其他线程可以再申请读锁,但是不能申请写锁
  • (2)如果某线程申请了写锁,则其他线程不能申请读锁,也不能申请写锁

定义读写锁对象的代码如下

pthread_rwlock_t rwlock;

初始化/损坏读写锁

ptread_rwlock_init()

函数使用属性attr来初始化rwlock引用的读写锁,如果attr为NULL,则使用默认的读写锁属性

int pthread_rwlock_init(pthread_rwlock_t *_restrict_rwlock, _const pthread_rwlockattr_t *_restrict _attr)

第一个参数rwlock是指向要初始化的读写锁的指针
第二个参数attr指向属性对象的指针,该属性对象要初始化的读写锁的特性,此函数如果设置为NULL,则使用默认的属性

pthread_rwlock_destroy()函数用来销毁读写锁int pthread_rwlock_destroy(pthread_rwlock_t *_rwlock)

两函数如果成功返回,将返回0.
申请读锁
pthread_rwlock_rdlock()函数以阻塞的方式来申请读锁

int pthread_rwlock_rdlock(pthread_rwlock_t *_rwlock)

pthread_rwlock_trylock()函数以非阻塞的方式来申请读锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *_rwlock);

如果不能申请到该读锁,pthread_rwlock_rdlock将阻塞当前进程,而pthread_rwlock_tryrdlock将返回错误
两函数执行成功后,将返回0
申请写锁
pthread_rwlock_wrlock()函数以阻塞的方式来申请写锁

int pthread_rwlocj_wrlock(pthread_rwlock_t *_rwlock)

pthread_rwlock_trywrlock()函数以非阻塞的方式来申请写锁

int pthread_rwlock_tryrwrlock(pthread_rwlock_t *_rwlock)

如果不能申请到该写锁,pthread_rwlock_wrlock将阻塞当前进程,而pthread_rwlock_trywrlock()将返回错误
在成功后,两函数将返回0
解锁
无论是读锁还是写锁都将使用函数pthread_rwlock_unlock()来释放锁

int pthread_rwlock_unlock(pthread_rwlock_t *_rwlock);

在使用此函数时需要注意一下情况

  • (1)如果调用此函数来释放读锁,但当前还有其他读写锁定,则保持读锁定状态,只不过当前线程已不再是其所有者之一,如果释放最后一个读锁,则读写锁将处理解锁状态
  • (2)如果调用此函数释放写锁,则置读写锁为解锁状态