应用场景:生产者消费者问题,是线程同步的一种手段。
必要性:为了实现等待某个资源,让线程休眠。提高运行效率
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); //等待资源
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); //等待资源,超时后自动退出
int pthread_cond_signal(pthread_cond_t *cond); //放出单个信号
int pthread_cond_broadcast(pthread_cond_t *cond); //广播形式放出信号
使用步骤:
初始化:
静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥量
或使用动态初始化
pthread_cond_init(&cond);
生产资源线程:
pthread_mutex_lock(&mutex);
开始产生资源
pthread_cond_sigal(&cond); //通知一个消费线程
或者
pthread_cond_broadcast(&cond); //广播通知多个消费线程
pthread_mutex_unlock(&mutex);
消费者线程:
pthread_mutex_lock(&mutex);
while (如果没有资源){ //防止惊群效应
pthread_cond_wait(&cond, &mutex);
}
有资源了,消费资源
pthread_mutex_unlock(&mutex);
注意:
1 pthread_cond_wait(&cond, &mutex),在没有资源等待是是先unlock 休眠,等资源到了,再lock
所以pthread_cond_wait he pthread_mutex_lock 必须配对使用。
2 如果pthread_cond_signal或者pthread_cond_broadcast 早于 pthread_cond_wait ,则有可能会丢失信号。
3 pthead_cond_broadcast 信号会被多个线程收到,这叫线程的惊群效应。所以需要加上判断条件while循环。
pcond.c:
#include#include #include #include pthread_cond_t hasTaxi = PTHREAD_COND_INITIALIZER;//初始化条件变量 pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥量 struct taxi{//出租车结构体 struct taxi *next;//指向下一个车的指针 int num;//出租车编号 }; struct taxi *Head=NULL;//出租车链表 void *taxiarv(void *arg){//生产者线程 printf("taxi arrived threadn"); pthread_detach(pthread_self()); struct taxi *tx; int i=1; while(1){ tx = malloc(sizeof(struct taxi));//车来了 tx->num = i; printf("taxi %d commingn",i); i++; pthread_mutex_lock(&lock);//互斥锁 tx->next = Head; Head = tx;//Head永远指向最新到的车 pthread_cond_signal(&hasTaxi);//发送信号有车了 pthread_mutex_unlock(&lock); sleep(1);//就是为了释放cpu } pthread_exit(0); } void *takeTaxi(void *arg){//消费者线程 printf("take taxi threadn"); pthread_detach(pthread_self()); struct taxi *tx; while(1){ pthread_mutex_lock(&lock); while(Head == NULL){//没有资源,继续等 pthread_cond_wait(&hasTaxi,&lock);//前面必须有lock函数 } tx =Head; Head = tx->next;//目前的状态是后来的车先走 printf("Take taxi %dn",tx->num);//哪个车走了 free(tx);//我理解为释放车位 pthread_mutex_unlock(&lock); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t tid1,tid2; pthread_create(&tid1,NULL,taxiarv,NULL); pthread_create(&tid2,NULL,takeTaxi,NULL); while(1){//不让主线程退出 sleep(1); } }
结果:
pcond.c:
#include#include #include #include pthread_cond_t hasTaxi = PTHREAD_COND_INITIALIZER;//初始化条件变量 pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥量 struct taxi{//出租车结构体 struct taxi *next;//指向下一个车的指针 int num;//出租车编号 }; struct taxi *Head=NULL;//出租车链表 void *taxiarv(void *arg){//生产者线程 printf("taxi arrived threadn"); pthread_detach(pthread_self()); struct taxi *tx; int i=1; while(1){ tx = malloc(sizeof(struct taxi));//车来了 tx->num = i; printf("taxi %d commingn",i); i++; pthread_mutex_lock(&lock);//互斥锁 tx->next = Head; Head = tx;//Head永远指向最新到的车 pthread_cond_signal(&hasTaxi);//发送信号有车了 pthread_mutex_unlock(&lock); sleep(1);//就是为了释放cpu } pthread_exit(0); } void *takeTaxi(void *arg){//消费者线程 printf("take taxi threadn"); pthread_detach(pthread_self()); struct taxi *tx; while(1){ pthread_mutex_lock(&lock); //while(Head == NULL)//没有资源,继续等 { pthread_cond_wait(&hasTaxi,&lock);//前面必须有lock函数 } tx =Head; Head = tx->next;//目前的状态是后来的车先走 printf("%d, Take taxi %dn",(int)arg,tx->num);//哪个车走了 free(tx);//我理解为释放车位 pthread_mutex_unlock(&lock); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t tid1,tid2; pthread_create(&tid1,NULL,taxiarv,NULL); sleep(5);//车来的早,人来得晚 pthread_create(&tid2,NULL,takeTaxi,(void*)1); while(1){//不让主线程退出 sleep(1); } }
结果:有没有 while(Head == NULL)//没有资源,继续等 的区别。有了,表不会空,所以积攒的出租车全部被人乘坐了。没有,线程会wait,必须来一辆走一辆,积攒的出租车无法被消费。
pcond.c: pthread_cond_signal 单个信号
#include#include #include #include pthread_cond_t hasTaxi = PTHREAD_COND_INITIALIZER;//初始化条件变量 pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥量 struct taxi{//出租车结构体 struct taxi *next;//指向下一个车的指针 int num;//出租车编号 }; struct taxi *Head=NULL;//出租车链表 void *taxiarv(void *arg){//生产者线程 printf("taxi arrived threadn"); pthread_detach(pthread_self()); struct taxi *tx; int i=1; while(1){ tx = malloc(sizeof(struct taxi));//车来了 tx->num = i; printf("taxi %d commingn",i); i++; pthread_mutex_lock(&lock);//互斥锁 tx->next = Head; Head = tx;//Head永远指向最新到的车 pthread_cond_signal(&hasTaxi);//发送信号有车了 pthread_mutex_unlock(&lock); sleep(1);//就是为了释放cpu } pthread_exit(0); } void *takeTaxi(void *arg){//消费者线程 printf("take taxi threadn"); pthread_detach(pthread_self()); struct taxi *tx; while(1){ pthread_mutex_lock(&lock); //while(Head == NULL)//没有资源,继续等 { pthread_cond_wait(&hasTaxi,&lock);//前面必须有lock函数 } tx =Head; Head = tx->next;//目前的状态是后来的车先走 printf("%d, Take taxi %dn",(int)arg,tx->num);//哪个车走了 free(tx);//我理解为释放车位 pthread_mutex_unlock(&lock); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t tid1,tid2; pthread_create(&tid1,NULL,taxiarv,NULL); //sleep(5);//车来的早,人来得晚 pthread_create(&tid2,NULL,takeTaxi,(void*)1); pthread_create(&tid2,NULL,takeTaxi,(void*)2); pthread_create(&tid2,NULL,takeTaxi,(void*)3);//三个乘客争夺一辆出租车 while(1){//不让主线程退出 sleep(1); } }
结果: 乱序,一次只发一个信号,每个信号谁抢到算谁的
pcond.c:
#include#include #include #include pthread_cond_t hasTaxi = PTHREAD_COND_INITIALIZER;//初始化条件变量 pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//初始化互斥量 struct taxi{//出租车结构体 struct taxi *next;//指向下一个车的指针 int num;//出租车编号 }; struct taxi *Head=NULL;//出租车链表 void *taxiarv(void *arg){//生产者线程 printf("taxi arrived threadn"); pthread_detach(pthread_self()); struct taxi *tx; int i=1; while(1){ tx = malloc(sizeof(struct taxi));//车来了 tx->num = i; printf("taxi %d commingn",i); i++; pthread_mutex_lock(&lock);//互斥锁 tx->next = Head; Head = tx;//Head永远指向最新到的车 //pthread_cond_signal(&hasTaxi);//发送信号有车了 pthread_cond_broadcast(&hasTaxi); pthread_mutex_unlock(&lock); sleep(1);//就是为了释放cpu } pthread_exit(0); } void *takeTaxi(void *arg){//消费者线程 printf("take taxi threadn"); pthread_detach(pthread_self()); struct taxi *tx; while(1){ pthread_mutex_lock(&lock); while(Head == NULL)//没有资源,继续等 { pthread_cond_wait(&hasTaxi,&lock);//前面必须有lock函数 } tx =Head; Head = tx->next;//目前的状态是后来的车先走 printf("%d, Take taxi %dn",(int)arg,tx->num);//哪个车走了 free(tx);//我理解为释放车位 pthread_mutex_unlock(&lock); } pthread_exit(0); } int main(int argc, const char *argv[]) { pthread_t tid1,tid2; pthread_create(&tid1,NULL,taxiarv,NULL); //sleep(5);//车来的早,人来得晚 pthread_create(&tid2,NULL,takeTaxi,(void*)1); pthread_create(&tid2,NULL,takeTaxi,(void*)2); pthread_create(&tid2,NULL,takeTaxi,(void*)3);//三个乘客争夺一辆出租车 while(1){//不让主线程退出 sleep(1); } }
结果: 广播形式发放信号
二、线程池概念和使用 概念:通俗的讲就是一个线程的池子,可以循环的完成任务的一组线程集合
必要性:我们平时创建一个线程,完成某一个任务,等待线程的退出。但当需要创建大量的线程时,假设T1为创建线程时间,T2为在线程任务执行时间,T3为线程销毁时间,当 T1+T3 > T2,这时候就不划算了,使用线程池可以降低频繁创建和销毁线程所带来的开销,任务处理时间比较短的时候这个好处非常显著。
线程池的基本结构:1 任务队列,存储需要处理的任务,由工作线程来处理这些任务
2 线程池工作线程,它是任务队列任务的消费者,等待新任务的信号
1创建线程池的基本结构:
任务队列链表
typedef struct Task;
线程池结构体
typedef struct ThreadPool;
2. 线程池的初始化:
pool_init()
{
创建一个线程池结构
实现任务队列互斥锁和条件变量的初始化
创建n个工作线程(只是固定大小的线程池)
}
3. 线程池添加任务
pool_add_task
{
判断是否有空闲的工作线程
给任务队列添加一个节点
给工作线程发送信号newtask
}
4. 实现工作线程
workThread
{
while(1){
等待newtask任务信号
从任务队列中删除节点
执行任务
}
}
5. 线程池的销毁
pool_destory
{
删除任务队列链表所有节点,释放空间
删除所有的互斥锁条件变量
删除线程池,释放空间
}
error: ‘ThreadPool {aka struct ThreadPool}’ has no member named ‘head’
意义:ThreadPool 结构体没有head这个成员。
解决:检查是否拼写错误。
error: too few arguments to function ‘pthread_mutex_init’
意思:pthread_mutex_init这个函数参数少了
解决:检查函数的参数,添加对应的参数
tpool.c:
#include#include #include #include #define POOL_NUM 10 //线程池数量 typedef struct Task{//任务结构体 void *(*func)(void *arg); void *arg; struct Task *next; }Task; typedef struct ThreadPool{//线程池结构体 pthread_mutex_t taskLock;//互斥量 pthread_cond_t newTask;//通知条件变量 pthread_t tid[POOL_NUM]; Task *queue_head;//任务 头部 int busywork;//表示可以工作的线程数 }ThreadPool; ThreadPool *pool; //工作线程 void *workThread(void *arg){ while(1){ pthread_mutex_lock(&pool->taskLock);//加锁 pthread_cond_wait(&pool->newTask,&pool->taskLock);//没有任务就阻塞 Task *ptask = pool->queue_head;//取出任务 pool->queue_head = pool->queue_head->next;//更新任务链表 pthread_mutex_unlock(&pool->taskLock);//解锁 ptask->func(ptask->arg);//执行函数任务 pool->busywork--;//可以工作的线程少了一个 } } //具体任务 void *realwork(void *arg){ printf("Finish work %dn",(int)arg); } //往线程池里添加任务 void pool_add_task(int arg){ Task *newTask; pthread_mutex_lock(&pool->taskLock);//访问临界资源必须加锁 while(pool->busywork>=POOL_NUM){//线程池满了 pthread_mutex_unlock(&pool->taskLock); usleep(10000);//休眠的时候一定要把锁释放,别的线程才能访问任务队列 pthread_mutex_lock(&pool->taskLock); } pthread_mutex_unlock(&pool->taskLock); newTask = malloc(sizeof(Task)); newTask->func = realwork;//要执行的任务函数(函数指针) newTask->arg = arg;//任务参数 pthread_mutex_lock(&pool->taskLock);//要插任务了,所以加锁 Task *member = pool->queue_head;//定义个指针方便插内容 if(member==NULL){ pool->queue_head = newTask;//列表没任务直接挂到头指针后 }else{ while(member->next!=NULL){ member=member->next; } member->next = newTask;//列表有任务,挂到任务列表尾部 } pool->busywork++;//可以工作的线程多了一个 pthread_cond_signal(&pool->newTask);//通知新任务来了 pthread_mutex_unlock(&pool->taskLock); } //线程池的初始化 void pool_init(){ pool = malloc(sizeof(ThreadPool)); pthread_mutex_init(&pool->taskLock,NULL); pthread_cond_init(&pool->newTask,NULL); pool->queue_head = NULL; pool->busywork=0; for(int i=0;i tid[i],NULL,workThread,NULL); } } //线程的销毁 void pool_destory(){ Task *head; while(pool->queue_head!=NULL){ head = pool->queue_head; pool->queue_head = pool->queue_head->next;//链表的删除 free(head); } pthread_mutex_destroy(&pool->taskLock);//销毁互斥量 pthread_cond_destroy(&pool->newTask);//销毁条件变量 free(pool); } int main(){ pool_init(); sleep(20); for(int i=1;i<=20;i++){ pool_add_task(i); } sleep(5); pool_destory(); }
结果:
如果不加等待的代码,任务来了直接传给线程 :
#include#include #include #include #define POOL_NUM 10 //线程池数量 typedef struct Task{//任务结构体 void *(*func)(void *arg); void *arg; struct Task *next; }Task; typedef struct ThreadPool{//线程池结构体 pthread_mutex_t taskLock;//互斥量 pthread_cond_t newTask;//通知条件变量 pthread_t tid[POOL_NUM]; Task *queue_head;//任务 头部 int busywork;//表示可以工作的线程数 }ThreadPool; ThreadPool *pool; //工作线程 void *workThread(void *arg){ while(1){ pthread_mutex_lock(&pool->taskLock);//加锁 pthread_cond_wait(&pool->newTask,&pool->taskLock);//没有任务就阻塞 Task *ptask = pool->queue_head;//取出任务 pool->queue_head = pool->queue_head->next;//更新任务链表 pthread_mutex_unlock(&pool->taskLock);//解锁 ptask->func(ptask->arg);//执行函数任务 pool->busywork--;//可以工作的线程少了一个 } } //具体任务 void *realwork(void *arg){ printf("Finish work %dn",(int)arg); } //往线程池里添加任务 void pool_add_task(int arg){ Task *newTask; newTask = malloc(sizeof(Task)); newTask->func = realwork;//要执行的任务函数(函数指针) newTask->arg = arg;//任务参数 pthread_mutex_lock(&pool->taskLock);//要插任务了,所以加锁 Task *member = pool->queue_head;//定义个指针方便插内容 if(member==NULL){ pool->queue_head = newTask;//列表没任务直接挂到头指针后 }else{ while(member->next!=NULL){ member=member->next; } member->next = newTask;//列表有任务,挂到任务列表尾部 } pool->busywork++;//可以工作的线程多了一个 pthread_cond_signal(&pool->newTask);//通知新任务来了 pthread_mutex_unlock(&pool->taskLock); } //线程池的初始化 void pool_init(){ pool = malloc(sizeof(ThreadPool)); pthread_mutex_init(&pool->taskLock,NULL); pthread_cond_init(&pool->newTask,NULL); pool->queue_head = NULL; pool->busywork=0; for(int i=0;i tid[i],NULL,workThread,NULL); } } //线程的销毁 void pool_destory(){ Task *head; while(pool->queue_head!=NULL){ head = pool->queue_head; pool->queue_head = pool->queue_head->next;//链表的删除 free(head); } pthread_mutex_destroy(&pool->taskLock);//销毁互斥量 pthread_cond_destroy(&pool->newTask);//销毁条件变量 free(pool); } int main(){ pool_init(); sleep(1); for(int i=1;i<=20;i++){ pool_add_task(i); } sleep(5); pool_destory(); }
结果: 因为多余的任务来了如果不等待,十个线程都在工作,但是signal还是直接传,但是大家都在忙,所以传过来的信号就丢失了,多余的任务没人做。
三、线程的GDB调试:显示线程
info thread
切换线程
thread id
GDB为特定线程设置断点
break location thread id
GDB设置线程锁,
set scheduler-locking on/off
on:其他线程会暂停。可以单独调试一个线程
off:next 时当前线程单步执行,但是其他线程直接全部执行
ttext.c:
#include#include void *testThread(void *arg){ char *threadName = (char*)arg; printf("Current running %sn",threadName); printf("aaaaaaaan"); printf("bbbbbbbbn"); pthread_exit(0); } int main(){ pthread_t tid1,tid2; pthread_create(&tid1,NULL,testThread,"thread1"); pthread_create(&tid2,NULL,testThread,"thread2"); pthread_join(tid1,NULL); pthread_join(tid2,NULL); }
执行过程有问题,可能跟老师版本不一致的原因。 学会用命令即可
作业:自己实现课程线程池代码,并提交实验截图。
见tpool.c



