线程
pthread_self():获取当前线程号
pthread_t pthread_self(void); 获取当前线程线程号
-lpthread.线程库没有在标准库中,所以加编译时动态加载库
main()函数创建的线程叫主线程:如果主线程结束,则其它线程会自动结束(无法运行)
pthread_create():创建子线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
@thread:用于存储新建线程号;
@att:线程属性;设置为NULL,使用系统默认属性创建线程,默认线程是联合态;
@start_routine:函数指针,线程实际要完成的任务;
@arg:是传给线程函数的数据;
union pthread_attr_t
{
char __size[__SIZEOF_PTHREAD_ATTR_T]; //字符数组:用于存储属性
long int __align; //字节对齐
};
pthread_join():线程等待
线程等待:主线程等待子线程
int pthread_join(pthread_t thread, void **retval); //相当于进程wait();
@thread:要等待的子线程线程号
@retval:接收子线程的退出状态
void pthread_exit():子线程退出;
子线程退出函数:
void pthread_exit(void *retval);
@retval:子线程的退出状态
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *pthread_fun_1(void *args)
{
//获取子线程的线程号
printf("%s start line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());
if(NULL != args)
{
int *p = (int *)args;
printf("int arg = %d\n",*p);
}
printf("%s stop line = %d\n",__func__,__LINE__);
//子线程退出
pthread_exit((void *)0x05);
}
int main()
{
//获取主线程的线程号
printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());
//创建子线程
pthread_t tid = 0;
int data = 10;
if(pthread_create(&tid,NULL,pthread_fun_1,&data) < 0)
{
perror("pthread_create error\n");
return -1;
}
#if 0 //等待方法一(不推荐使用)
sleep(1);
#endif
#if 1 //等待方法二
#if 0 //默认退出状态
pthread_join(tid,NULL);//可以保证子线程顺利运行,NULL为默认为联合态
#endif
#if 1 //确定退出状态
int *status = NULL;
pthread_join(tid,(void **)&status);
printf("status =%p\n",status);
#endif
#endif
printf("%s stop line = %d,pthread_self = %lu,tid = %lu\n",__func__,__LINE__,pthread_self(),tid);
}
线程分离
默认是联合态:在主线程pthread_join()过程中,如果接收到子线程的退出信号,则主线程立即回收子线程的堆栈空间
但是在做服务器时,因为主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收;
线程主要属性:
Thread attributes:
Detach state = PTHREAD_CREATE_DETACHED
Scope = PTHREAD_SCOPE_SYSTEM
Inherit scheduler = PTHREAD_EXPLICIT_SCHED
Scheduling policy = SCHED_OTHER
Scheduling priority = 0
Guard size = 0 bytes
Stack address = 0x40197000
Stack size = 0x3000000 bytes
解决办法:线程分离:在分离态的子线程退出,由系统立即回收空间
分离办法一:在创建新线程时将线程属性设置为分离
1.声明线程属性变量:pthread_attr_t
2.初始化线程属性:
int pthread_attr_init(pthread_attr_t *attr);
3.将属性设置为分离属性:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 设置分离属性
@detachstate:
PTHREAD_CREATE_DETACHED 分离
PTHREAD_CREATE_JOINABLE 联合
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 获取分离属性
4.在线程结束时将属性销毁
int pthread_attr_destroy(pthread_attr_t *attr);
分离方法二:创建线程运行过程中设置为分离
int pthread_detach(pthread_t thread);
@thread:线程号
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *pthread_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
if(NULL != args)
{
int *p = (int *)args;
printf("%s %d args = %d\n",__func__,__LINE__,*p);
}
printf("%s %d stop\n",__func__,__LINE__);
pthread_exit((void *)0x5);
}
int main()
{
//获取主线程的线程号
printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());
//创建子线程
pthread_t tid = 0;
int data = 10;
//声明线程属性变量
pthread_attr_t attr = {0};
//初始化线程属性
pthread_attr_init(&attr);
//将属性设置为分离属性
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
{
perror("pthread_create error\n");
return -1;
}
#if 0 //确定退出状态 等待 由主线程回收子线程的堆栈空间
int *status = NULL;
pthread_join(tid,(void **)&status);//使用地址值保存子线程的退出状态
printf("status =%p\n",status);
#else //进行线程分离后 由系统回收 解决了在主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收问题;
int i;
for(i = 0; i < 10; i++)
{
printf("main : pthread_self = %lu\n",pthread_self());
sleep(1);
}
#endif
//在线程结束时销毁属性
pthread_attr_destroy(&attr);
}
方法二
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *pthread_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
if(NULL != args)
{
int *p = (int *)args;
printf("%s %d args = %d\n",__func__,__LINE__,*p);
}
printf("%s %d stop\n",__func__,__LINE__);
pthread_exit((void *)0x5);
}
int main()
{
//获取主线程的线程号
printf("%s line = %d ,pthread_self = %lu\n",__func__,__LINE__,pthread_self());
//创建子线程
pthread_t tid = 0;
int data = 10;
//创建子线程
if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
{
perror("pthread_create error\n");
return -1;
}
//设置指定子线程为分离
if(pthread_detach(tid) != 0)
{
perror("ptread_detach error");
return -1;
}
#if 0 //确定退出状态 等待 由主线程回收子线程的堆栈空间
int *status = NULL;
pthread_join(tid,(void **)&status);
printf("status =%p\n",status);
#else //进行线程分离后 由系统回收 解决了在主线程while(1),不会pthread_join(),则会造成子线程的堆栈空间无法立即回收问题;
int i;
for(i = 0; i < 10; i++)
{
printf("main : pthread_self = %lu\n",pthread_self());
sleep(1);
}
#endif
}
pthread_cleanup_push()、pthread_cleanup_pop():线程退出处理函数
注册线程退出处理函数:
void pthread_cleanup_push(void (*routine)(void *),void *arg);
@routine:退出处理函数:
@arg:传给退出处理函数的数据
退出处理函数的执行:
void pthread_cleanup_pop(int execute);
@execute:个数
#define pthread_cleanup_push(routine, arg)
do {
__pthread_cleanup_class __clframe (routine, arg)
/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
If EXECUTE is non-zero, the handler function is called. */
#define pthread_cleanup_pop(execute) \ 所以一定要成对调用pthread_cleanup_pop(),否则编译出错;
__clframe.__setdoit (execute);
} while (0);
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void pthread_exit_fun_1(void *args)
{
printf("%s args = %p\n",__func__,args);
}
void pthread_exit_fun_2(void *args)
{
printf("%s args = %p\n",__func__,args);
}
void *pthread_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
pthread_cleanup_push(pthread_exit_fun_1,args); //注册
pthread_cleanup_push(pthread_exit_fun_2,args + 1);
if(NULL != args)
{
int *p = (int *)args;
printf("%s %d args = %d\n",__func__,__LINE__,*p);
}
printf("%s %d stop\n",__func__,__LINE__);
#if 1
pthread_cleanup_pop(1); //执行
pthread_cleanup_pop(1);
#endif
pthread_exit(NULL);
return NULL;
}
int main()
{
printf("main start: pthread_self = %lu\n",pthread_self());
pthread_t tid = 0;
int data = 110;
if(pthread_create(&tid,NULL,pthread_fun,&data) < 0)
{
perror("pthread_create error");
return -1;
}
pthread_join(tid,NULL); //使用地址值保存子线程的退出状态
printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}
创建多个子线程
创建多个子线程:多pthread_create()对应多个线程函数,也可对应同一个线程函数,pthread_join()多次
子线程间对于资源的使用是共享的,但是不共享线程的堆栈空间
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static int data = 100;
void *pthread_fun1(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
data += 5;
printf("%s data = %d\n",__func__,data);
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
void *pthread_fun2(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
data -= 6;
printf("%s data = %d\n",__func__,data);
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
int main()
{
printf("main start: pthread_self = %lu\n",pthread_self());
pthread_t tid = 0,tid2 = 0;
pthread_create(&tid,NULL,pthread_fun1,NULL);
pthread_create(&tid2,NULL,pthread_fun2,NULL);
pthread_join(tid,NULL);
pthread_join(tid2,NULL);
data += 2;
printf("%s data = %d\n",__func__,data);
printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}
解决线程并发时的竞争方法一:互斥锁
解决线程并发时的竞争方法一:互斥锁
typedef union
{
struct __pthread_mutex_s
{
int __lock;
unsigned int __count;
int __owner;
int __kind;
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;
1.声明互斥锁:全局
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; //立即锁
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; //递归锁
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; //错误检查
2.初始化锁:创建线程之前main();
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
@mutex:互斥锁指针
@mutexatrr:互斥锁属性
3.加锁:线程函数中:操作数据前
int pthread_mutex_lock(pthread_mutex_t *mutex); //加阻塞锁 如果有线程已经加锁,则阻塞,直到另一线程解锁,才能加锁成功,继续运行
int pthread_mutex_trylock(pthread_mutex_t *mutex); //加非阻塞锁 非阻塞锁:加锁不成功不会阻塞,轮询
4.解锁:线程函数中:操作数据后
int pthread_mutex_unlock(pthread_mutex_t *mutex);
5.销毁锁:子线程运行结束时pthread_join()后
int pthread_mutex_destroy(pthread_mutex_t *mutex);
#include <pthread.h>
#include <stdio.h>
int main()
{
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int ret = pthread_mutex_init(&mutex,NULL);
printf("pthread_mutex_init ret = %d\n",ret);
ret = pthread_mutex_lock(&mutex);
printf("pthread_mutex_lock ret = %d\n",ret);
printf("data option\n");
ret = pthread_mutex_unlock(&mutex);
printf("pthread_mutex_unlock ret = %d\n",ret);
ret = pthread_mutex_destroy(&mutex);
printf("pthread_mutex_destroy ret = %d\n",ret);
}
使用互斥锁解决生产者消费者问题中的竞争问题
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#if 0
使用互斥锁解决生产者消费者问题中的竞争问题
死锁:多进程或多线程并发时,对同一对共享资源产生了循环等待;
使用非阻塞锁解决死锁问题
#endif
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static int pcount = 1000;
void *product_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
#if 0
pthread_mutex_lock(&mutex); //加阻塞锁
#endif
if(pthread_mutex_trylock(&mutex))
{
sleep(1);
continue;
}
pcount++;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
pthread_mutex_unlock(&mutex);
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
void *constom_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
#if 0
pthread_mutex_lock(&mutex); //加阻塞锁
#endif
if(pthread_mutex_trylock(&mutex))
{
sleep(1);
continue;
}
pcount--;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
pthread_mutex_unlock(&mutex);
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
int main()
{
printf("main start: pthread_self = %lu\n",pthread_self());
pthread_mutex_init(&mutex,NULL);
pthread_t tid = 0,tid2 = 0;
pthread_create(&tid,NULL,product_fun,NULL);
pthread_create(&tid2,NULL,constom_fun,NULL);
pthread_join(tid,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&mutex);
printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}
条件变量使用步骤和相关函数
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
条件变量使用步骤和相关函数:
1.声明条件变量(声明互斥锁)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2.初始化条件变量(初始化互斥锁)
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
3.达到某条件时等待(当前线程调用):
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); //有超时的等待,会等待指定时间,如果在指定时间内有唤醒信号则唤醒,如果到达指定时间还没有唤醒信号则停止等待
struct timespec {
__kernel_time_t tv_sec; /* seconds abs time*/
long tv_nsec; /* nanoseconds */
};
成功返回0,如果超时返回ETIMEDOUT,
4.唤醒处理等待队列中的某线程(不能自己唤醒自己):
int pthread_cond_signal(pthread_cond_t *cond); //只唤醒一个
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有
5.销毁条件变量(销毁互斥锁)
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量的特性:
1.先等待后唤醒
2.只能当前线程唤醒其它线程,不能唤醒自己
int main()
{
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&mutex,NULL);
int ret = pthread_cond_init(&cond,NULL);
printf("pthread_cond_init ret = %d\n",ret);
#if 0
ret = pthread_cond_wait(&cond,&mutex);
printf("pthread_cond_wait ret = %d\n",ret);
#endif
#if 1
struct timespec timsec = {0};
timsec.tv_sec = time(NULL) + 3;
ret = pthread_cond_timedwait(&cond,&mutex,&timsec);
if(ret == 0)
{
printf("option data\n");
}
else if(ret == ETIMEDOUT)
{
printf("pthread_cond_timedwait timeout\n");
}
else
printf("other error\n");
#endif
ret = pthread_cond_signal(&cond);
printf("pthread_cond_signal ret = %d\n",ret);
pthread_mutex_destroy(&mutex);
ret = pthread_cond_destroy(&cond);
printf("pthread_cond_destroy ret = %d\n",ret);
}
使用条件变量解决生产者消费者并发时的竞争问题
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#if 0
使用条件变量解决生产者消费者并发时的竞争问题
#endif
static int pcount = 1000;
static char hasproduct = 0; //是否有产品
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void *product_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
if(hasproduct) //如果有产品就等待
pthread_cond_wait(&cond,&mutex);
else //如果没有产品就生产
{
pcount++;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
hasproduct = 1; //生产后就有产品
pthread_cond_signal(&cond); //唤醒消费者消息
}
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
void *constom_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
if(!hasproduct) //如果没有产品就等待
pthread_cond_wait(&cond,&mutex);
else //如果有产品就消费
{
pcount--;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
hasproduct = 0;
pthread_cond_signal(&cond); //唤醒生产者生产
}
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
int main()
{
printf("main start: pthread_self = %lu\n",pthread_self());
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_t tid = 0,tid2 = 0;
pthread_create(&tid,NULL,product_fun,NULL);
pthread_create(&tid2,NULL,constom_fun,NULL);
pthread_join(tid,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}
信号量解决线程并发
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#if 0
信号量:非负整数(同一时间能够访问共享资源的线程数)
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
} sem_t;
PV原语:
1.声明信号量:sem_t
2.初始化信号量:
int sem_init(sem_t *sem, int pshared, unsigned int value);
@pshared:0表示同一进程内的线程共享;
@value:初始化的信号量值:
3.p操作:
int sem_wait(sem_t *sem); //阻塞版p操作
int sem_trywait(sem_t *sem);//非阻塞版p操作
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); //带超时时间的p操作
4.v操作
int sem_post(sem_t *sem);
5.销毁
int sem_destroy(sem_t *sem);
6.获取信号量值
int sem_getvalue(sem_t *sem, int *sval);
@sval:存储获取到的信号量值
#endif
int main()
{
sem_t sem = {0};
int ret = sem_init(&sem,0,1);
int value = 0;
ret = sem_getvalue(&sem,&value);
printf("sem_getvalue ret = %d,value = %d\n",ret,value);
ret = sem_wait(&sem); //p操作
#if 0
ret = sem_wait(&sem); //p操作,阻塞版p操作
#endif
#if 0 //非阻塞p操作,轮询
while((ret = sem_trywait(&sem)))
{
printf("sem_wait ret = %d\n",ret);
sleep(1);
}
#endif
#if 0
struct timespec timsec = {0};
timsec.tv_sec = time(NULL) + 5;
ret = sem_timedwait(&sem,&timsec);
if(ret == 0)
printf("data opion");
else if(ret == -1)
{
printf("timeout\n");
sem_destroy(&sem);
return -1;
}
#endif
printf("sem_wait ret = %d\n",ret);
ret = sem_getvalue(&sem,&value);
printf("sem_getvalue ret = %d,value = %d\n",ret,value);
printf("data option\n");
ret = sem_post(&sem); //v操作
ret = sem_getvalue(&sem,&value);
printf("sem_getvalue ret = %d,value = %d\n",ret,value);
ret = sem_destroy(&sem);
printf("ret = %d\n",ret);
}
使用信号量解决生产者消费者问题
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
#if 0
使用信号量解决生产者消费者问题:
1.解决并发(多个任务同时(同一时间点)运行):可以有多进程,也可以使用多线程;
1)线程占用资源少(进程有独立的.text等,多个线程共享同一进程的.text等);
2)线程响应时间短
3)线程间切换比进程切换快,效率高
2.并发时竞争问题解决:
#endif
static int pcount = 1000;
static sem_t sem = {0};
void *product_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
sem_wait(&sem);
pcount++;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
sem_post(&sem);
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
void *constom_fun(void *args)
{
printf("%s %d tid = %lu start\n",__func__,__LINE__,pthread_self());
while(1)
{
sem_wait(&sem);
pcount--;
printf("%s %d tid = %lu pcount = %d\n",__func__,__LINE__,pthread_self(),pcount);
sem_post(&sem);
}
printf("%s %d tid = %lu stop\n",__func__,__LINE__,pthread_self());
}
int main()
{
printf("main start: pthread_self = %lu\n",pthread_self());
sem_init(&sem,0,1);
pthread_t tid = 0,tid2 = 0;
pthread_create(&tid,NULL,product_fun,NULL);
pthread_create(&tid2,NULL,constom_fun,NULL);
pthread_join(tid,NULL);
pthread_join(tid2,NULL);
sem_destroy(&sem);
printf("main stop: pthread_self = %lu,tid = %lu\n",pthread_self(),tid);
}