栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

L5进程、线程和进程间通信--条件变量和线程池(day5)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

L5进程、线程和进程间通信--条件变量和线程池(day5)

一、条件变量

应用场景:生产者消费者问题,是线程同步的一种手段。
必要性:为了实现等待某个资源,让线程休眠。提高运行效率

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;itid[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;itid[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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934542.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号