线程
Unix 环境高级编程原配代码
https://github.com/can130/apue3e/tree/master/threads
线程标示
线程的身份id 使用 pthread_t 数据类型表示
头文件 : pthread.h
//比较连个线程id是否相等的函数
//相等返回非0 数值 否则 返回0
int pthread_equal(pthread_t tid1,pthread_t tid2);
//获取自身线程id
pthread_t pthread_self(void);
//线程创建 成功返回0 否则返回错误编号
//创建成功返回时 线程id将被设置成tidp所指向的内存单元
//attr 线程属性 创建一个具有默认属性的线程时设置为NULL
//start_rtn 线程开始运行函数地址
//arg 上述函数传递的参数有一个以上时 放入一个结构中 并把地址传入
int pthread_create(pthread_t * restrict tidp,
const pthread_attr_t * restrict attr,
void *(*start_rtn)(void *),void *restrict arg);
//线程终止
//进程中任意线程调用 exit _EXIT _exit 整个进程终止
//单个线程终止的三种方式
//1.线程从启动例程中返回,返回值是整个线程的退出码
//2.线程可以被同一进程中的其他线程取消
//3.线程自身调用pthread_exit
//rval_ptr : 进程中的其他线程可以通过pthread_join函数访问到这个指针
void pehtead_exit(void *rval_ptr);
//调用线程将一直阻塞 直到指定的线程调用终止 若返回 val_ptr包含返回码 //若线程被取消 指定的内存单元将被设置为 PTHREAD_CANCEL
//如果对返回值不感兴趣 可以将 val_ptr 设置为NULL
//成功 返回 0 否则返回错误编号
int pthread_join(pthread_t thread,void **val_ptr);
//请求取消同一进程中的其他线程
//技能效果 同 tid 线程调用了参数为 PTHREAD_CANCEL的pthread_exit函数
//成功返回0 否则返回错误编号
int pthread_cancel(pthread_t tid);
//线程为了避免被无缘无故杀死后没有时间料理后事 可以使用以下函数
//线程清理处理函数 thread cleanup handler
//一个线程可以调用多个线程清理函数
//线程清理函数放在栈中 也就是说 执行顺序与注册想反相反
void pthread_cleanup_push(void (*rtn)(void *),void *arg);
void pthread_cleanup_pop(int execute);
//上面两个函数必须以成对的函数形式出现 rtn在线程执行以下动作时由pthread_cleanup_push 函数调度
//1.调用pthread_exit
//2.响应取消请求时
//3.用非零excute参数调用pthread_cleanup_pop时
//线程正常返回将不会调用清理函数
线程同步
互斥量
//动态初始化互斥量 attr为属性 NULL为默认属性 成功返回0 否则返回错误编号
int pthread_mutex_init(pthread_mutex_init *restrict mutex,
const pthread_mutexatt_r *restrict attr);
//释放互斥量
int phread_mutex_destroy(pthread_mutex_t *mutex);
//互斥量 加锁 成功返回0 否则返回错误编号
//效果 对互斥量加锁 如果互斥量已经上锁 调用线程阻塞至互斥量被解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//对互斥量尝试加锁
//若未锁 则对该锁加锁 返回0 否则返回 EBUSY 不阻塞线程
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
避免死锁
线程对一个互斥量加锁两次 陷入死锁
两个线程各占有一个互斥量 并尝试锁住对方的互斥量时 产生死锁
解决方案
1.对需要被同时加锁的互斥量按照总以相同的顺序加锁
2.使用pthread_mutex_trylock避免死锁
//带有时间的互斥量 成功返回0 否则错误编号
//到达超时时间时(绝对时间) 不会对互斥量加锁 而是返回错误码ETIMEDOUT
//可用于避免永久阻塞
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
读写锁 共享互斥锁
读写锁与互斥量相似,不过允许更高的并行性
读写锁可以有3种状态:
读模式下加锁状态,写模式写加锁状态,不加锁状态
读模式加锁可读不可写 写模式加锁不可读写
读模式加锁时收到写模式加锁请求 将拒绝后来的读模式加锁避免拥挤读模式加锁
以下函数除特殊声明外 均成功返回0 否则返回错误编码
//初始化读写锁 默认属性将attr设置为NULL
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
//释放读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//读模式加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//写模式加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//释放锁
int pthread_mutex_unlock(pthread_rwlock_t *rwlock);
//对应的另外两个加锁函数
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
带有超时读写的读写锁 绝对时间
使用时需要包含头文件 time.h
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict tsptr);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,const struct timespec *restrict tsptr);
条件变量 (其实不太懂 干嘛的 此部分 未完善)
可用的另一种同步机制,与互斥量一起使用允许以无竞争的方式等待特定条件的发生,条件变了本身由互斥量保护,线程在改变条件状态之前必须锁住互斥量
//初始化
//静态
pthread_cond_t cond_name = PTHREAD_COND_INITIALIZER;
//动态 使用默认属性将 attr设置为NULL
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
//等待
//等待条件变量变为真,如果在特定时间内不能满足,那么会生成一个返回错误码的变量
int pthread_cond_wait(pthread_cond_t *restrict aond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthead_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict tsptr);
传递给pthread_cond_wait 的互斥量对条件进行保护,调用者把锁住的互斥量传给函数然后自动把调用线程放在等待条件的线程列表上,对互斥量进行解锁
自旋锁
与互斥量类似 但不使线程休眠,而是处于忙等待 用于锁持有时间短 线程不希望在重新调度上花费太多成本
套路都一样
自旋锁只有一个属性 共享属性 设为PTHREAD_PROCESS_SHARED能被锁底层内存线程锁获取 设置为 PTHREAD_PROCESS_PRIVATE只能被初始化该锁的进程内部的线程访问
int pthread_spin_init(pthread_spinlock_t *lock,int pshared);
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);
屏障
用于协调多个线程并行工作的同步机制 允许每个线程等待,直到所有的合作线程都达到某一点 然后继续执行 例子中开了8个线程对800万个数字排序
int pthread_barrier_init(pthread_barrier_t *restrict barrier,const pthread_barrierattr_t *restrict attr,unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);
初始化时可以使用count参数指定,在允许所有线程继续运行之前,必须达到屏障的线程数目 ep:设置为9 则有9个线程调用了pthread_barrier_wait 就能向前
int pthread_barrier_wait(pthread_barrier_t *barrier);
成功返回 0 或者 PTHREAD_BARRIER_SERIAL_THREAD(只有一个线程返回这个值 其他返回0 返回这个值的线程可以作为主线程 工作在其他线程已经完成的工作结果上)
一旦到达计数值 屏障可以被重用