做以个一元购的网站多少钱,网站开发需求分析用的图,做电子元器件销售什么网站好,优书网所有书单#x1f525;博客主页#xff1a; 我要成为C领域大神#x1f3a5;系列专栏#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享#xff0c;与更多的人进行学习交流
线程概述
… 博客主页 我要成为C领域大神系列专栏【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞收藏⭐评论✍️ 本博客致力于知识分享与更多的人进行学习交流
线程概述
线程是进程中的一个执行单元
经典的并发模型多进程模型占用大量的内存开销庞大的进程间调度开销提供一种开销更小更轻量级的并发解决方案多线程技术。
线程在进程中与进程共享资源和数据进程是容器线程是执行单元进程退出可能导致所有线程退出。 每个进程中都有一个主控线程还有其他的普通线程操作系统会给进程分配需要的内存单元。 不支持线程技术的操作系统可以忽略线程概念进程是调度单位。
多线程操作系统下进程是内存管理单位线程才是唯一的调度单元。
线程的CPU分配
Linux操作系统下线程就是轻量级进程每个进程分配一个LWP
在Linux操作系统下所有的调度单位都是进程淡化线程概念
线程的实现方式
用户线程User Thread在用户空间实现的线程不是由内核管理的线程是由用户态的线程库来完成线程的管理。可以利用第三方库的形式在不支持线程技术的系统下安装和使用线程用户级线程的调度开销更小因为大部分的操作都在用户层完成无需内核干预
内核线程Kernel Thread操作系统中每创建一个线程系统都会为其创建一个内核对象此线程系统可以识别支持会为线程执行资源(时间片)。Control控制器会为每个内核级线程创建内核对象每个内核级线程都会由CPU进行时间片切换
轻量级进程LightWeight Process在内核中来支持用户线程
进程的蜕化
进程内存资源独占不与其他人共享进程是独立的调度单位(无需考虑线程问题)
进程中出现了多个执行单元讨论考虑线程问题
讨论和分析的蜕化讨论线程进程的讨论变为主线程和普通线程的分析和讨论
线程间的共享资源和非共享资源 PCB共享资源
栈线程栈空间非共享每个线程创建后会分配8MB线程栈
库库资源共享
堆共享堆空间
全局资源和静态资源全局资源共享
代码段代码段共享
文件描述符表文件描述符表共享
信号的处理行为(某个线程改变信号行为)对所有线程共享。信号屏蔽字非共享普通线程拥有独立的屏蔽字拷贝继承于主线程
TCB每个线程拥有自己的线程控制块独立的tid
线程开发相关API接口
NPTL线程库
NPTL线程库是典型的内核级线程库创建的线程可以被系统标识分配cpu资源(轻量级进程)
native Posix thread library
相关命令
ps aux 进程查看
ps ajx 进程关系查看
ps -eLf 所有线程查看 PID相同的是一组线程 LWP 与 PID 相同的情况表示该进程只有一个线程。
ps -Lf pid 查看某一个进程下的线程 每个线程都会被系统分配lwp 调度编号 便于系统管理调度线程但是线程拥有独立的tid线程id
头文件和函数
#include pthread.h使用线程函数的头文件
pthread_t tid线程tid类型
pthread_create
创建线程并指定类型pthread_create(pthread_t *tid,NULL,void * (thread_job)(void *),void *arg)第一个参数是线程id第二个是线程属性第三个参数是线程工作地址第四个参数是线程工作参数。 返回值是err 下面是使用pthread_create创建线程的简单demo程序通过PID和LWP相同可以看出这个线程就是主控线程 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{printf(普通线程被创建参数为%d\n,*(int*)arg);return NULL;
}int main()
{pthread_t tid;int code1024;//普通线程都是由主控线程创建的pthread_create(tid,NULL,thread_job,(void*)code);printf(普通线程被主控线程创建出来\n);while(1) sleep(1);return 0;
} 在编译时需要链接库
gcc Pthrad_Create.c -lpthread -o app 关于线程函数的错误处理
线程函数出错会返回错误号err0使用char *errstrstrerrorerr进行错误判断
下面是打印错误日志的demo程序 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{//printf(普通线程被创建参数为%d\n,*(int*)arg);while(1) sleep(1);return NULL;
}int main()
{pthread_t tid;int code1024;int flags0;int err;//普通线程都是由主控线程创建的while(1){if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}else{printf(线程%d成功创建\n,flags);}}return 0;
} 32位Ubuntu操作系统下一个进程只能创建381个线程
pthread_self
pthread_tid pthread_self(void) 成功返回当前线程的id
主线程通过pthread_create创建普通线程成功传出tid与普通线程自身利用pthread_self()得到的tid值相等但是进程状态不相等因为pthread_self()获取tid时可以保证当前的有效性主线程在获取tid的时候普通线程可能已经退出。 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());return NULL;
}int main()
{pthread_t tid;int code1024;int flags0;int err;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);while(1) sleep(1);//主控线程还在运行普通线程已经退出return 0;
} pthread_join(pthread_t tid,void **retval)
线程回收函数可以回收线程资源的同时获取线程的返回值。经典的阻塞回收函数会一致等待普通线程结束后进行回收。 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());return (void*)126;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);pthread_join(tid,revtal);printf(Thread Exit Code:%ld\n,(long int)revtal);return 0;
} 可以看到如果线程工作函数可以正常退出退出码就是线程工作函数的返回值 pthread_cancel(pthread_t tid)
指定线程tid取消结束线程
下面是简单的取消一个线程的demo程序 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{//printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());while(1){printf(进程正在工作。。。\n);sleep(1);}return (void*)129;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause();return 0;
} 线程被成功结束这个进程下只有一个主控线程在工作 当我们对代码稍作修改
删除线程工作函数中的调用系统函数 void * thread_job(void *arg)//普通线程的创建
{//printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());while(1){}return (void*)129;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause(); return 0;
} 结果显示pthread_cancel并没有成功取消线程。
这有些类似于信号信号处理的三个切换条件系统调用软件中断软件异常。而pthread_cancel取消事件的处理条件必须有系统调用。
有一个专门为pthread_cancel提供系统调用的空函数void pthread_testcancel(void)提供一次空的调用。
在线程工作函数加入这个函数再次尝试 void * thread_job(void *arg)//普通线程的创建
{//printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pause(); return 0;
} 可以看到只要一个主控线程普通线程被结束了。 当我们对线程进行取消后回收一下资源打印退出码看一下 void * thread_job(void *arg)//普通线程的创建
{//printf(普通线程tid %x 创建成功\n,(unsigned int)pthread_self());while(1){pthread_testcancel(void);}return (void*)129;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);sleep(5);pthread_cancel(tid);pthread_join(tid,revtal);printf(Thread Exit Code:%ld\n,(long int)revtal);pause(); return 0;
} 可以看到退出码是-1所以线程指定退出码时不允许使用-1保留给pthread_cancel。 pthread_detach(pthread_t tid)
用于将一个线程设置为分离态线程。
可以通过线程属性批量创建分离态线程这些线程诞生即是分离态。
线程有回收态和分离态这两种状态是互斥的。
若修改线程退出状态从回收态改为分离态此操作不可逆不能将分离线程变为回收态 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include pthread.hvoid * thread_job(void *arg)//普通线程的创建
{pthread_detach(pthread_self());while(1){pthread_testcancel();}return (void*)129;
}int main()
{pthread_t tid;int code1024;int flags0;int err;void *revtalNULL;//普通线程都是由主控线程创建的if((errpthread_create(tid,NULL,thread_job,(void*)code))0){printf(thread create failed%s\n,strerror(err));exit(0);//创建失败进程退出}printf(Monster Thread Tid:%x 普通线程Tid:%lx \n,(unsigned int)pthread_self(),tid);if((errpthread_join(tid,revtal))0){printf(join call failed %s\n,strerror(err));}printf(Thread Exit Code:%ld\n,(long int)revtal);pause();return 0;
} join和detach会竞争时间片所以有时线程以detach分离态被创建当线程处于分离态时无法手动回收join执行失败 如果对一个分离态线程进行回收操作pthread_join会失效返回。对一个已经处于回收阶段join已经开始阻塞等待了的线程设置分离分离设置失败。
一个自行回收一个由系统回收一个可以得到线程退出码一个无法获取
pthread_exit(void * exitcode)
线程退出函数结束当前线程与进程无关退出码可以被join获取
线程的退出方式
return 0
主线程结束进程结束普通线程返回结果
pthread_cancel
主线程结束与进程无关
普通进程结束与进程无关
使用普通线程对主线程进行回收产生了僵尸级线程 pthread_exit()
主线程结束与进程无关
普通进程结束与进程无关
exit()
进程退出释放所有线程
线程属性
在使用创建线程函数时指定线程类型pthread_create(pthread_t *tid,pthread_attr_t *attr,void * (thread_job)(void *),void *arg)
用第二个参数指定线程属性
pthread_attr_t线程属性类型是一个结构体类型 修改线程程属性的操作 使用pthread_attr_getdetachstate查看线程默认退出属性
使用pthread_attr_getstack查看栈属性 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include signal.h
#include pthread.hint main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_getdetachstate(attr,State);if(StatePTHREAD_CREATE_JOINABLE)printf(Thread Default State is Join\n);elseprintf(Thread Default State is Detach\n);pthread_attr_getstack(attr,stack_addr,stacksize);printf(线程栈地址为%p 栈大小为%d\n,stack_addr,stacksize);return 0;
} 使用pthread_attr_setdetachstate修改线程默认退出状态为分离态 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include signal.h
#include pthread.hvoid *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_getdetachstate(attr,State);if(StatePTHREAD_CREATE_JOINABLE)printf(Thread Default State is Join\n);elseprintf(Thread Default State is Detach\n);pthread_attr_getstack(attr,stack_addr,stacksize);printf(线程栈地址为%p 栈大小为%d\n,stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED);pthread_create(tid,attr,thread_job,NULL);int errpthread_join(tid,NULL);if(err0)printf(Join Call Failed:%s\n,strerror(err));return 0;
} 使用pthread_attr_setstacksize修改默认线程栈大小 #include stdio.h
#include stdlib.h
#include unistd.h
#include fcntl.h
#include sys/types.h
#include string.h
#include sys/fcntl.h
#include signal.h
#include pthread.hvoid *thread_job(void * arg)
{}int main()
{int State;void *stack_addr;//栈空间地址size_t stacksize;//栈空间默认大小pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_getdetachstate(attr,State);if(StatePTHREAD_CREATE_JOINABLE)printf(Thread Default State is Join\n);elseprintf(Thread Default State is Detach\n);pthread_attr_getstack(attr,stack_addr,stacksize);printf(线程栈地址为%p 栈大小为%d\n,stack_addr,stacksize);pthread_t tid;pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED);stacksize0x100000;int err;int flags0;while(1){if((stack_addr(void*)malloc(stacksize))NULL){printf(malloc Stack Failed\n);}pthread_attr_setstacksize(attr,stacksize);if((errpthread_create(tid,attr,thread_job,NULL))0){printf(Create failed:%s\n,strerror(err));exit(0);}printf(Thread %d 创建成功\n,flags);}return 0;
} 由结果可知当将默认大小变小后32位系统下可以创建的线程数量增加了。