一.线程退出函数
线程退出
pthread_exit()
void pthread_exit(void *retval);
1.功能
线程调用这个函数时,可以主动退出(终止)
它和exit()很像,exit()是终止整个程序,而pthread_exit()是终止次进程
如果在次进程里面调用错误,调用的是exit,整个线程终止。
2.返回值
成功返回0,失败返回非0值
如果返回值很多时,就会封装成一个结构体,返回值结构体变量的地址
3.参数
retval:线程结束的返回值
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int num = 5;
void *thread1(void *arg)
{
//int num=5;
while(1)
{
printf("hello world\n");
sleep(1);
pthread_exit(&num);
//pthread_exit(NULL);
}
}
int main()
{
pthread_t id;
if( pthread_create(&id,NULL,thread1,NULL)!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
sleep(1);
int *num;
pthread_cancel(id);
pthread_join(id,(void *)(&num) );
printf("pthread is exit %d\n", *((int *)num));
return 0;
}
pthread_exit(NULL)直接退出
return() 直接退出
注册线程退出处理函数
void pthread_cleanup_push(void (*routine)(void *),void *arg);
void pthread_cleanup_pop(int execute);
弹栈 return 0不会弹栈
弹栈线程退出处理函数的几种条件
pthread_cleanup_push( ! 0 ) 主动弹栈 0是不弹栈
如果线程是被别人调用pthread_cancel取消的,也会弹栈
如果调用pthread_exit函数也是会主动弹栈
**注意:**return退出的话是不会自动弹栈的,想要自动弹栈pthread_cleanup_pop(!0)
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int num = 5;
void thread_exit(void *arg)
{
printf("my exit\n");
}
void *thread1(void *arg)
{
pthread_cleanup_push(thread_exit,NULL);
//int num=5;
while(1)
{
printf("hello world\n");
sleep(1);
//pthread_exit(&num);
pthread_exit(NULL);
//return (void *)NULL;
}
pthread_cleanup_pop(!0);
}
int main()
{
pthread_t id;
if( pthread_create(&id,NULL,thread1,NULL)!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
sleep(1);
int *num;
pthread_cancel(id);
pthread_join(id,(void *)(&num) );
printf("pthread is exit %d\n", *((int *)num));
return 0;
}
二.线程分离函数 信号量 互斥锁 条件变量 PV操作
线程等待的目的:
1.保证线程的退出顺序:保证一个线程退出并且回收资源后允许下一个进程退出
2.回收线程退出时的资源情况:保证当前线程退出后,创建的新线程不会复用刚才退出线程的地址空间
3.获得新线程退出时的结果是否正确退出返回值
线程的状态
可结合态:这种状态下的线程是能被其他进程回收资源或被杀死的
可分离态:不能被其他进程回收资源或被杀死的,它存储的资源在它终止时由系统自动回收
默认情况下,线程是可结合态
线程分离函数
pthread_detach()
int pthread_detach(pthread_t thread);
1.功能:如果次线程不希望别人调用pthread_join函数来回收,而是希望自己在运行结束时,自动回收资源调用pthread_detach
将pthread_detach()中的线程变为分离态
2.返回值:成功返回0.,错误返回非0值
3.参数:thread:需要分离的那个次线程的TID
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int num = 5;
void *thread1(void *arg)
{
//int num=5;
while(1)
{
printf("hello world\n");
sleep(1);
pthread_exit(&num);
//pthread_exit(NULL);
}
}
int main()
{
pthread_t id;
if( pthread_create(&id,NULL,thread1,NULL)!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
pthread_detach(id);
sleep(1);
int *num;
pthread_cancel(id);
pthread_join(id,(void *)(&num) );
printf("pthread is exit %d\n", *((int *)num));
return 0;
}
tach_struct 进程信息(所有的进程号pid,地址空间,调度优先级)存放在这个结构体中
线程切换的开销低,因为它实质上是函数的切换共用进程地址空间
进程和线程的区别
1.进程是资源分配的最小单位,线程是资源调度的最小单位
2.每个进程有独立的地址空间,多个线程共享进程的地址空间,线程间的切换比进程间的切换开销小
3.线程的调度必须通过频繁的加锁来保持线程的同步,影响线程的并发性能
4.进程比线程更加健康,多进程之间相互独立,一个进程的异常对其他进程无影响,而一个线程的崩溃可能影响其他线程或整个程序
5.线程之间的通信方便(小数据量的),同一进程下,线程共享全局变量还有静态变量等数据,而进程之间的通信需要以通信的方式进行(IPC),不过如何处理好同步与互斥,是编写多线程程序的难点
6.多线程的代码结构比多进程的代码结构简单易读
线程同步:互斥锁,线程信号量,条件变量
互斥锁:
1.定义一个互斥锁(变量) pthread_mutex_t mutex;
2.初始化锁:预设互斥锁的初始值 pthread_mutex_mutex = PTHREAD_MUTEX_INITIALIZER; (不常用)
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
功能:初始化定义的互斥锁(就是设置互斥锁所需要的值)
返回值:总是返回0,所以这个函数不需要进行错误处理
参数:
mutex:互斥锁,需要我们自己定义
3.加锁解锁
pthread_mutex_lock(&mutex);(阻塞加锁)访问临界区加锁操作
pthread_mutex_trylock(&mutex);(非阻塞加锁)当锁被占用时返回EBUSY而不是挂起等待
pthread_mutex_unlock(&mutex);访问临界区解锁
4.进程退出时销毁互斥锁
pthread_mutex_destroy(&mutex);
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct message
{
int fd1;
pthread_mutex_t mutex;
};
pthread_mutex_t mutex;
void *thread1(void *arg)
{
//struct message msg= *( (struct message *)arg);
//int fd = msg.fd1;
int fd = *( (int *)arg );
while(1)
{
pthread_mutex_lock(&mutex);
write(fd,"hello",5);
write(fd,"world\n",6);
//sleep(1);
pthread_mutex_unlock(&mutex);
}
}
void *thread2(void *arg)
{
//struct message msg= *( (struct message *)arg);
int fd = *( (int *)arg );
while(1)
{
pthread_mutex_lock(&mutex);
write(fd,"hhhhh",5);
write(fd,"wwwww\n",6);
//sleep(1);
pthread_mutex_unlock(&mutex);
}
}
int main()
{
//struct message msg;
int fd = open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644);
if(fd<0)
{
perror("open error");
exit(1);
}
//msg.fd1 = fd;
pthread_mutex_init(&mutex,NULL);
pthread_t id;
if( pthread_create(&id,NULL,thread1,(void *)(&fd))!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
pthread_t id2;
if( pthread_create(&id2,NULL,thread2,(void *)(&fd))!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
//pause();
pthread_join(id,NULL);
pthread_join(id2,NULL);
return 0;
}
线程信号量:
线程信号量使用步骤:
1.定义信号量集合
sem_t sem[3]
线程信号量集合其实是一个数组,数组每个元素就是一个信号量(sem[0]:第一个信号量,sem[1] 第二个信号量,sem[2]第三个信号量)
2.初始化集合中的每个信号量
#include <semaphore.h>
int sem_init(sem_t*sem,int pthread,unsigned int value);
功能:初始化线程信号量集合中的某个信号量,给它设置一个初值
返回值:成功返回0,失败返回-1,errno被设置
信号量的错误号不是返回的,而是直接设置到errno
参数:value:初始化值(对于二进制信号量来说,要么是1,要么是0)
pshare:0:给线程使用,!0可以给进程使用
3.P V操作
P:int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
V:int sem_post(sem_t *sem);
sem_wait value
假如你获得的时候别人已经获得这个信号量(value值减一),你阻塞(在内核中实现加减)
4.进程结束时,删除线程信号量集合
sem_destriy(&sem[]);
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
struct message
{
int fd1;
pthread_mutex_t mutex;
};
pthread_mutex_t mutex;
sem_t sem[1];
void *thread1(void *arg)
{
//struct message msg= *( (struct message *)arg);
//int fd = msg.fd1;
int fd = *( (int *)arg );
while(1)
{
//pthread_mutex_lock(&mutex);
sem_wait(&sem[0]);
write(fd,"hello",5);
write(fd,"world\n",6);
//sleep(1);
//pthread_mutex_unlock(&mutex);
sem_post(&sem[0]);
}
}
void *thread2(void *arg)
{
//struct message msg= *( (struct message *)arg);
int fd = *( (int *)arg );
while(1)
{
//pthread_mutex_lock(&mutex);
sem_wait(&sem[0]);
write(fd,"hhhhh",5);
write(fd,"wwwww\n",6);
//sleep(1);
//pthread_mutex_unlock(&mutex);
sem_post(&sem[0]);
}
}
int main()
{
//struct message msg;
int fd = open("./a.txt",O_RDWR|O_CREAT|O_APPEND,0644);
if(fd<0)
{
perror("open error");
exit(1);
}
//msg.fd1 = fd;
pthread_mutex_init(&mutex,NULL);
sem_init(&sem[0],0,1);
pthread_t id;
if( pthread_create(&id,NULL,thread1,(void *)(&fd))!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
pthread_t id2;
if( pthread_create(&id2,NULL,thread2,(void *)(&fd))!=0 )
{
perror("pthread_creat error!\n");
exit(1);
}
//pause();
pthread_join(id,NULL);
pthread_join(id2,NULL);
return 0;
}
练习:线程1输出11111,线程2输出22222,线程3输出33333
11111
22222
33333
11111
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
pthread_mutex_t mutex;
pthread_cond_t cond1,cond2,cond3;
sem_t sem[3];
void *test1(void *arg)
{
while(1)
{
sem_wait(&sem[0]);
//pthread_mutex_lock(&mutex);
//pthread_cond_wait(&cond1);
printf("11111\n");
sem_post(&sem[1]);
//pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void *test2(void *arg)
{
while(1)
{
sem_wait(&sem[1]);
//pthread_mutex_lock(&mutex);
//pthread_cond_wait(&cond2,&mutex);
printf("22222\n");
sem_post(&sem[2]);
//pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void *test3(void *arg)
{
while(1)
{
sem_wait(&sem[2]);
//pthread_mutex_lock(&mutex);
//pthread_cond_wait(&cond3,&mutex);
printf("33333\n");
sem_post(&sem[0]);
//pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_t id1,id2,id3;
sem_init(&sem[0],0,1);
sem_init(&sem[1],0,0);
sem_init(&sem[2],0,0);
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond1,NULL);
pthread_cond_init(&cond2,NULL);
pthread_cond_init(&cond3,NULL);
pthread_create(&id1,NULL,test1,NULL);
pthread_create(&id2,NULL,test2,NULL);
pthread_create(&id3,NULL,test3,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond1);
pthread_cond_destroy(&cond2);
pthread_cond_destroy(&cond3);
sem_destroy(&sem[0]);
sem_destroy(&sem[1]);
sem_destroy(&sem[2]);
return 0;
}
条件变量(和互斥锁配合使用)
一条线程做自加,一条线程输出(当自加到5输出之后清零)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int count = 0;
pthread_mutex_t mutex;
void * add1(void * arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
count++;
sleep(1);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void *print(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
if( count <=5 ) printf("%d\n",count);
else count = 0;
sleep(1);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_t id1;
pthread_t id2;
int ret= pthread_create(&id1,NULL,add1,NULL);
if(0 != ret)
{ perror("pthread create error");exit(0); }
int ret2= pthread_create(&id2,NULL,print,NULL);
if(0 != ret2)
{ perror("pthread create error");exit(0); }
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
条件变量的主要作用:(线程协同)主线程对count+1,此线程发现当count == 5 时输出count的值并将count清零
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
int count = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void * add1(void * arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
count++;
if(count == 5)
{
pthread_cond_signal(&cond);
}
sleep(1);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void *print(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
if( count !=5 )
{
pthread_cond_wait(&cond,&mutex);
}
printf("%d\n",count);
sleep(1);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_t id1;
pthread_t id2;
pthread_cond_init(&cond,NULL);
int ret= pthread_create(&id1,NULL,add1,NULL);
if(0 != ret)
{ perror("pthread create error");exit(0); }
int ret2= pthread_create(&id2,NULL,print,NULL);
if(0 != ret2)
{ perror("pthread create error");exit(0); }
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}
多线程配合工作时,当线程检测到某条件不满足时就休眠,直到别的线程将条件准备好,然后通过条件变量将其叫醒
条件变量:
使用步骤
1.定义一个条件变量
pthread_cond_t= PTHREAD_COND_INITIALIZER;
2.初始化
pthread_cond_init(pthread_cond_t *restrict, const pthread_condattr *restrict attr);
功能
初始化条件变量,与互斥锁化类似
返回值:
成功返回0,不成功返回非0错误号
参数
cond:条件变量
3.使用条件变量
4.删除条件变量,也需要把互斥锁删除
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
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_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
三.线程池 任务队列
多线程存在的问题:
1.进程所支持的线程数量问题(受限)
2.线程的创建和销毁的开销问题
1.任务队列为空
2.任务队列为满
3.任务队列不为空
任务队列为空时,线程池里的线程阻塞等待
任务队列不为空时,线程池里的线程处理任务
任务队列为满时,不能添加新的任务
任务队列的大小
任务队列的锁
int thread_num; //已开启的线程数量
struct job *head;//任务队列的头指针
struct job *tail;//任务队列的尾指针
int queue_max_num;//任务队列的最大数
int queue_cur_num;//任务队列已有多少任务
pthread_t *pthread_ids;//保存线程池中线程id
pthread_cond_t queue_empty;///任务队列为空的条件
pthread_cond_t queue_not_empty;//任务队列部位空的条件
pthread_cond_t queue_not_full;//任务队列不为满的条件
每个线程输出helloword和welcome to China
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
struct job
{
void *(*func)(void *arg);
void *arg;
struct job *next;
};
struct threadpool
{
int thread_num;//已开启的线程数量
pthread_t *pthread_ids;//保存线程池中线程id
struct job *head;//任务队列的头指针
struct job *tail;//任务队列的尾指针
int queue_max_num;//任务队列的最大数
int queue_cur_num;//任务队列已有多少任务
pthread_mutex_t mutex;
pthread_cond_t queue_empty;//任务队列为空的条件
pthread_cond_t queue_not_empty;//任务队列不为空的条件
pthread_cond_t queue_not_full;//任务队列不为满的条件
};
//线程函数
void *threadpool_function( void * arg)
{
struct threadpool *pool = (struct threadpool *) arg ;
struct job *pjob = NULL;
while(1)
{
pthread_mutex_lock( &(pool->mutex) );
while(pool->queue_cur_num == 0) //任务队列是否为空
{
pthread_cond_wait( &(pool->queue_not_empty), &(pool->mutex) );
}
pjob = pool->head;
pool->queue_cur_num --;
if(pool->queue_cur_num != pool->queue_max_num)
{
pthread_cond_broadcast( &(pool->queue_not_empty) );
}
if ( pool->queue_cur_num==0 )
{
pool->head = pool->tail = NULL ;
pthread_cond_broadcast( &(pool->queue_empty) );
}
else
{
pool->head = pool->head->next ;
}
pthread_mutex_unlock( &(pool->mutex) );
(*(pjob->func))(pjob->arg);
free(pjob);
pjob = NULL ;
}
}
//线程池初始化
struct threadpool *threadpool_init( int thread_num,int queue_max_num)
{
struct threadpool *pool=( struct threadpool * )malloc( sizeof(struct threadpool) );
//malloc
//int thread_num = 20;
//int queue_max_num = 100;
pool->thread_num = thread_num ;
pool->queue_max_num = queue_max_num;
pool->queue_cur_num = 0;
pool ->head = NULL;
pool->tail = NULL;
pthread_mutex_init( &(pool->mutex) ,NULL );
pthread_cond_init( &(pool->queue_empty), NULL );
pthread_cond_init( &(pool->queue_not_empty) , NULL );
pthread_cond_init( &(pool->queue_not_full) , NULL );
pool->pthread_ids = (pthread_t *) malloc( sizeof(pthread_t) * thread_num );
//malloc
for ( int i = 0; i < pool->thread_num ; i++)
{
pthread_create( &(pool->pthread_ids[i ]) , NULL , threadpool_function , (void *)pool );
}
return pool;
}
//添加任务函数
void threadpool_add_job(struct threadpool *pool,void *func(void *) , void * arg )
{
pthread_mutex_lock(&(pool->mutex));
while(pool->queue_cur_num == pool->queue_max_num)
{
pthread_cond_wait( &(pool->queue_not_full), &(pool->mutex) );
}
struct job *pjob = (struct job *)malloc ( sizeof(struct job));
//malloc
pjob->func = func ;
pjob->arg = arg;
//pjob->func(pjob->arg);
if(NULL == pool->head)
{
pool->head = pool->tail =pjob;
pthread_cond_broadcast(&(pool->queue_not_empty) );
}
else
{
pool->tail ->next = pjob;
pool->tail = pjob;
}
pool->queue_cur_num ++;
pthread_mutex_unlock( &(pool->mutex) );
}
//任务函数
void * work ( void * arg )
{ //在这个函数中添加任务
char * p =(char *)arg;
printf("hello world %s\n",p);
printf("welcome to china %s\n",p);
sleep(1);
}
//资源回收
void threadpool_destroy (struct threadpool *pool )
{
pthread_mutex_lock(&(pool->mutex) );
while( pool->queue_cur_num != 0 )
{
pthread_cond_wait( &(pool->queue_empty) , &(pool->mutex) );
}
pthread_mutex_unlock( &(pool->mutex) );
pthread_cond_broadcast(&(pool->queue_empty) );
pthread_cond_broadcast(&(pool->queue_not_empty) );
pthread_cond_broadcast(&(pool->queue_not_full) );
free(pool->pthread_ids);
for( int i = 0 ; i< pool->thread_num ; i++)
{
pthread_cancel(pool->pthread_ids[i] );
pthread_join(pool->pthread_ids[i] ,NULL );
}
struct job *temp;
while(pool->head !=NULL )
{
temp = pool->head;
pool->head = temp->next;
free(temp);
}
free(pool);
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->queue_empty));
pthread_cond_destroy(&(pool->queue_not_empty));
pthread_cond_destroy(&(pool->queue_not_full));
}
int main()
{
struct threadpool * pool = threadpool_init( 10,100 ); //一次运行10个线程
threadpool_add_job( pool , work , "1" );
threadpool_add_job( pool , work , "2" );
threadpool_add_job( pool , work , "3" );
threadpool_add_job( pool , work , "4" );
threadpool_add_job( pool , work , "5" );
threadpool_add_job( pool , work , "6" );
threadpool_add_job( pool , work , "7" );
threadpool_add_job( pool , work , "8" );
threadpool_add_job( pool , work , "9" );
threadpool_add_job( pool , work , "10" );
threadpool_add_job( pool , work , "11" );
threadpool_add_job( pool , work , "22" );
threadpool_add_job( pool , work , "33" );
threadpool_add_job( pool , work , "44" );
threadpool_add_job( pool , work , "55" );
threadpool_add_job( pool , work , "66" );
threadpool_add_job( pool , work , "77" );
threadpool_add_job( pool , work , "88" );
threadpool_add_job( pool , work , "99" );
threadpool_add_job( pool , work , "100" );
sleep(50);
threadpool_destroy(pool );
return 0;
}