简述多线程
Pthread数据类型
数据类型 | 描述 |
---|
phtread_t | 线程ID | pthread_mutex_t | 互斥对象 | pthread_mutexattr_t | 互斥属性对象 | pthread_cond_t | 条件变量 | pthread_condattr_t | 条件变量的属性对象 | pthread_key_t | 线程特有数据的键 | pthread_once_t | 一次性初始化控制上下文 | pthread_attr_t | 线程的属性对象 |
Pthreads函数返回值
- 从系统调用和库函数中返回状态,传统的做法是:返回0表示成功,返回-1表示失败,并设置errno值以标识错误原因,
- Pthread则:返回0表示成功,返回任意正值表示失败,这一失败的返回值与errno中值的含义相同。
线程相关API
创建线程
#include <pthread.h>
int pthread_create(pthread_t *pthread, const pthread_attr_t *attr, void *(strat)(void*), void *arg);
- pthread: 保存线程ID
- attr: 传入线程各个属性设置,NULL则为默认属性
- 新线程通过调用带有参数arg的函数start而开始执行。
线程终止
线程终止方式:
- 线程start函数执行return语句并返回指定值
- 线程调用pthread_exit()
- 调用pthread_cancel()取消线程
- 任意线程调用了exit() ,或主程序执行了return,都会导致进程中的所有线程立即终止。
pthread_exit() 将立即终止程序,且返回值可由另一线程通过pthread_join()来获取。
#include <pthread.h>
void ptrhead_exit(void *retval);
retval所指向的内容不应分配与线程栈中,防止失效。
线程ID
进程内部的每一个线程都有一个唯一标识,称为线程ID。 获取线程ID的方法:
- pthread_create() 返回给调用者
- pthread_self()函数获得自己的线程ID
pthread_self()
#include <pthread.h>
pthread_t pthread_self(void);
线程ID的作用:
- 不同的Pthreads函数利用线程ID来标识要操作的目标线程。(pthread_join\pthread_detach()\pthread_cancel()\pthread_kill())
pthread_equal() 检查两个线程ID是否相同 (ID相同则返回非0,不同则返回0)
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
连接(joining)已终止的线程
函数**ptrhead_join()**等待由pthred_t标识的线程终止。 如果线程已经终止,ptrhead_join()会立即返回。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- 若retval为非空指针,将会保存线程终止时返回值的拷贝,该返回值亦即线程调用return 或者 pthread_exit()时所指定的值。
- 向pthread_join传入一个之前已然连接过的线程ID,将会导致无法预知的行为。
- 若线程并未分离,则必须使用pthread_join()来进行连接。如果未能连接,那么线程终止时将产生僵尸线程。除了浪费系统资源以外,僵尸线程若积累过多,应用将再也无法创建新的线程。
线程的分离
默认情况下,线程是可连接的,线程退出时,可通过pthread_join获得返回状态,但当不关心返回状态时,则可以设置为分离属性,让系统自动清理移除。
#include <pthread>
int pthread_detach(pthread_t thread);
常见用法:pthread_detach(pthread_self());
线程同步
保护对共享变量的访问:互斥量
- 静态分配的互斥量,初始化静态互斥量PTHREAD_MUTEX_INITIALIZER
#include <pthread.h>
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
-
防止互斥量死锁的方案:
- 定义互斥量的层级关系,总是以相同顺序对改组互斥量进行锁定。
- 较少使用:尝试一下,然后恢复。 pthread_mutex_trylock(); 如果trylock失败,则将释放该线程所有互斥量。
-
动态初始化互斥量
#include <pthread.h>
int pthread_mutex-init(pthread_mutex_t *mutex, const pthread_mutexattr_t * attr);
使用场景:
- 动态分配于堆中的互斥量。例如,动态创建针对某一结构的链表,表中每个结构都包含一个pthread_mutex_t类型字段来存放互斥量,借以保护对该结构的访问。
- 互斥量是在栈中分配的自动变量
- 初始化经由静态分配,且不使用默认属性的互斥量。
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
通知状态的改变:条件变量
- 静态条件变量:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- 条件变量的主要操作是发送信号(signal)和等待(wait)。
- 发送信号操作通知一个或多个处于等待的线程,某个共享变量的状态已经改变。
- 等待操作是指在收到一个通知前一直处于阻塞状态。
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cont_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
pthread_cond_signal() 和 pthread_cond_broadcast() 差别在于二者对于阻塞pthread_cond_wait()的多个线程处理方式不同。 pthread_cond_signal()只保证唤醒至少一条遭到阻塞的线程 pthread_cond_broadcast() 则会唤醒所有遭到阻塞的线程。
pthread_cont_timewait() 指定等待通知的休眠上线。
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
线程取消
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- 取消状态
PTHREAD_CANCEL_DISABLE,不允许取消,阻塞 PTHREAD_CANCEL_ENABLE, 允许取消 - 类型
PTRHEAD_CANCEL_ASYNCHRONOUS,可能会在任何时间点取消 PTHREAD_CANCEL_DEFERED,挂起,直到到达取消点 - 设置函数
#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);
#include <pthread.h>
void pthread_tescancel(void);
#include <pthread.h>
void pthread_cleanup_push(void (*routime)(void*), void *arg);
void pthread_cleanup_pop(int execute);
当execute不为0,则表示清理函数一定会执行。
|