概念
一个线程拥有表示线程执行环境的信息:
线程id
一组寄存器值,栈,
信号屏蔽字,
errno变量
线程私有变量
进程的所有信息对线程共享
代码段,栈,堆,文件描述符!
线程测试宏_POSIX_THREADS
线程标识
线程用线程id标识,pthread_t
pthead_t,在不同系统中看实现而定,so可能是结构体,为确保可移植性,最好不要当整数来处理
so比较时,用pthread_equal函数比较是否相等
#include<pthread.h>
int pthread_equal(pthread_t p1, pthread_t p2);//相等返回!0, 不等返回0
#include <pthread.h>
pthread_t pthread_self(void);//返回想线程自身id
线程只在进程上下文中才有效!
线程创建
#include<pthread.h>
int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void * (*fun)(void *), void arg);//成功返回0,错误返回函数返回的错误码
创建的id通过 tid返回,可能在create返回前,指定的函数就执行,so不要在执行函数中用共享的进程中的tid取当前进程值
attr 指定线程的类型
fun 指定的函数的执行起始地址
arg,向fun传递参数是,将 参数保存在结构体,fun通过arg传递的地址取相应参数的值
新线程继承调用线程的浮点环境和,屏蔽信号字,但挂起信号被擦出
信号在创建是要休眠,否则,主线从退出,进程就会退出!
写读,写写
一般的写的存储器周期,可能比读存储器周期长,读写同时进行,可能会导致周期交叉,导致数据出错
互斥量
当对某偶一个,互斥量加锁时,其他欲锁此变量的线程被阻塞
当一个线程释放互斥量时,所有阻塞线程变为可运行状态,第一个为可运行的线程可加锁,运行,其他依旧阻塞
同步只对加锁的线程有效,其他无锁可访问的线程也会导致一致性
#include<pthread.h>
int pthread_mutex_init(pthread_mutext_t *mutex, const pthread_mutex_attr *restrict attr);//成功返回0,否则返回错误码
int pthread_mutex_destory(ptread_mutex *mutex);//成功返回0,否则返回错误码
使用mutex之前必须初始化!
动态初试话mutex(使用malloc),attr 为NULL使用默认初始化
so必须使用pthread_mutex_destory 配对销毁信号量
可以用PTHREAD_MUTEX_INITIALIZER静态初始化mutex
#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutx);//已经被锁,阻塞线程,成功返回0,否则返回错误码
int pthread_mutex_trylock(pthread_mutex_t *mutx);//mutex已经被锁,直接返回EBUSY,不希望线程被阻塞用此锁
int pthread_unlock(pthread_mutex_t *mutx);//解锁互斥量
死锁避免
当出现多个线程一相反的顺序锁住互斥量时,很可能会发生死锁
so使各个线程以相同的顺序锁住互斥量,避免死锁
有时因为程序结构,对互斥量排序比较困难
可以采用pthread_mutex_terylock,当锁住第一个mutex时才继续前进,否则释放获得的锁,一段时间再试!
锁的粒度是一个需要考虑的问题,若粒太大,则可能串行等待,不能很好并发
太细,加锁解锁耗时,编程复杂度大!
pthread_mutex_timedlock
设置绝对时间,阻塞到指定时间(过了呢?实验!)
读写锁
读写锁可以理解为共享互斥锁!相对互斥量,一次只能一个线程持有处理,并发性更好!
加读锁时,其它线程能加读锁,不能加写锁(被阻塞)-------共享方式加读锁
加写锁时,其他不能加读锁也不能加写锁(被阻塞)--------互斥方式加写锁
一般系统实现时,在申请写锁后会阻塞线程读锁的请求,以防止大量的读操作时写锁一直不能获得!(适用于读操作远大于写操作情况)
初始化
#include<pthread.h>
int pthread_init(pthread_rwlock_t *rwlock, const pthread_rwlock_attr_t *restrict attr);
若使用默认参数,attr设为NULL!
int pthread_destory(pthread_rwlock_t rwlock);
成功返回0 ,失败返回错误编号
如果在线程持有锁时,释放锁,会导致不可预知的结果!
信号量必须用destory函数销毁,进行资源回收,才能释放内存,否则会是的资源泄漏(锁相关资源,具体待查!)
也可以使用PTHREAD_RWLOCK_INITIALIZER静态初始化!
加锁解锁
#include<pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//加读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//加写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//解读/写 锁
成功返回0,否则错误编号
不阻塞加锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//加读锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//加写锁
成功返回0,否则返回EBUSY
超时的读写锁
条件变量
条件变量,wait使用一方,等待signal产生信号是才能继续执行
wait将当前线程放入等待队列阻塞,当siagnal时唤醒继续执行
signal,唤醒等待队列的线程,若无等待队列则信号不保存(放空炮)
#include <pthread.h>
pthread_cond_wait(pthread_cond_t *cndnd, pthread_mutex_t *mutex);
等待之前必须锁住mutex,在之后语句中unlock mutex!(1)
wait在将线程放如等待队列之后unlock mutex ,使得不至于当前线程在未进入等待队列之前,就产生产品,而队列中无等待者,使得等待线程永久等待!
在wait返回后会lock mutex,接下来的(1)unlock生效!
在等待之前一定要判断等待条件,
有产品则不等待,防止生产者先执行,signal次数不够,因为signal信号不会累加!
若无产品,则直接等待signal唤醒即可
在唤醒后,可直接操作产品,因为接下来的语句都是在获得了mutex之后的不会冲突
int pthread_cond_signal(pthread_cond_t *cond);
唤醒一个线程
一定要在生产完之后才能signal
生产时一定要mutex
int pthread_cond_signal(pthread_cond_t *cond);
唤醒所有线程
#include <iostream>
#include <pthread.h>
using namespace std;
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_t tid1,tid2 ,tid3;
int flag = 0;
void* fun(void *)
{
for(int i = 0; i < 2; i++)
{
pthread_mutex_lock(&mutex);
cout << "thread==>"<<pthread_self()<<"waiting"<<endl;
while(flag <= 0)pthread_cond_wait(&cond, &mutex);//有产品则不等待,无产品则等待唤醒,等待时释放锁
//到此wait退出前 加锁mutex
cout << "thread==>"<<pthread_self()<<endl;
flag --;
pthread_mutex_unlock(&mutex);
cout << "thread==>"<<pthread_self()<<"out=>"<<i<<endl;
}
return (void *)0;
}
void *creator(void *)
{
for(int i = 0; i < 4; i++)
{
pthread_mutex_lock(&mutex);
flag ++;//记录产品数
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
cout << "creator created 4 signal"<<endl;
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond, NULL);
pthread_create(&tid1, NULL,creator, NULL);
pthread_create(&tid2, NULL,fun, NULL);
pthread_create(&tid3, NULL,fun, NULL);
sleep(10);
cout << "Hello world!" << endl;
return 0;
}
屏障
使得指定数量的线程都到达某一指定点时,所有线程才能继续执行,否则等待
# include <pthread.h>
int phread_barrier_init(pthread_barrier *restrict barrier, const pthread_barrierattr_t *restrict attr, int count);
当count数量的到达时才能继续 执行
int phread_barrier_destory(pthread_barrier *barrier);
成功返回0,否则错误编码
int pthread_barrier_wait(pthread_barrier *barrier);
表示本线程已经到达等待点,等其他线程干上来
barrier在所有线程都赶上后,可重用,但必须现destory再 init,保证count值