Linux线程的创建与回收

线程创建

1.为什么引入线程

  • 进程在切换时系统开销大
  • 很多做操作系统引入了轻量级进程(LWP)
  • 同一线程共享相同的地址空间
  • Linux本质上不区分进程、线程

2. 进程与线程的区别

  • 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。这里主要强调独立进程有独立的地址空间,Linux为每个进程创建task_struct。每个进程都参与内核调度,互不影响。

  • 线程是进程的一个实体, 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈,下面会细说),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

  • 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

3. 使用线程的好处

  • 大大提高了任务切换的效率
  • 避免了额外的TLB&cache的刷新
每个线程共享的资源					每个线程私有的资源
 - 可执行的指令						  - 线程ID(TID)
 - 静态数据							  - PC(程序计数器)和相关寄存器
 - 进程中打开的文件描述符		  		  - 堆栈
 - 当前的工作路径					  	  - 错误号(errno)
 - 用户ID							  - 优先级
 - 用户组ID							  - 执行状态和属性

pthread_creat函数

#include <pthread.h>
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,
void *(*start_rtn)(void*),void *arg);
/*C99中将第一个参数限定为restrict  restrict 限定符,表明指针是访问一个数据对象的唯一且初始的方式.即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码.
*/
  • 成功时返回0,失败时返回错误码
  • 第一个参数 : thread 线程对象
  • 第二个参数 : 线程属性,NULL代表默认属性
  • 第三个参数 :线程执行的函数
  • 第四个参数 :执行函数的参数

这里着重说一下第二个参数,我们可以看到第二个参数的类型是pthread_attr_t指针类型下面具体说一下

typedef struct
{
    
    
       int    detachstate;           //线程的分离状态
       int    schedpolicy;            // 线程调度策略
       structsched_param      schedparam;    //线程的调度参数
       int    inheritsched;          //线程的继承性
       int    scope;                 //线程的作用域
       size_t     guardsize;         //线程栈末尾的警戒缓冲区大小
       int    stackaddr_set;
       void*    stackaddr;           //线程栈的位置
       size_t     stacksize;          //线程栈的大小
}pthread_attr_t;
  • __detachstate —— 表示新线程是否与进程中其他线程脱离同步,如果置位则新线程不能用pthread_join()来同步与回收,且在退出时自行释放所占用的资源。缺省为 PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为 PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到 PTHREAD_CREATE_JOINABLE状态。这个属性与线程回收相关,下面会细讲两种状态的区别。
  • __schedpolicy——表示新线程的调度策略,主要包括 SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和 SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过 pthread_setschedparam()来改变。
  • __schedparam——一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR 或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
  • __inheritsched——有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和 调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
  • __scope——表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值: PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同 进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
  • __stacksize——表示线程栈的大小,若不指定则默认为ulimit 中的 stack大小。一般来说是8388608字节。如果是嵌入式开发的话,一般需要通过pthread_attr_setstacksize重新设置值。否则可能创建失败返回12错误码(ENOMEM ).。

pthread_attr_t结构中还有一些值,但不使用pthread_create()来设置。

线程回收

pthread_join函数 && pthread_detach函数

int pthread_join(pthread_t thread, void **retval);int pthread_join(pthread_t thread, void **retval);

这里线程的回收与进程的类似。一个线程对应一个pthread_join()调用,对同一个线程进行多次pthread_join()调用是逻辑错误。join函数只能回收 PTHREAD_CREATE_JOINABLE状态的线程。其他状态不可以。当线程处于PTHREAD_CREATE_JOINABLE状态时,如果线程结束运行还没有被回收,则它的 状态类似于进程中的Zombie Process(僵尸进程),还有一部分的资源没有被系统回收(退出状态码),所以要调用pthread_join函数等待要结束的线程,并可以的到线程退出的状态码。
那么上述就可以发生线程没执行完,就执行到join函数,调用者会被阻塞,直到线程执行完成并且回收。但是有时候我们不希望因线程未执行完而被阻塞。比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以在子线程中加入代码

pthread_detach(pthread_self())

或者父线程调用

pthread_detach(thread_id)//(非阻塞,可立即返回)

这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。

最后关注我,点个赞再走吧(๑>؂<๑)

参考博客

[1] https://blog.csdn.net/tongxinhaonan/article/details/42558561
[2] http://zhaojunjie.blog.51cto.com/5475365/1033968
[3] https://baike.baidu.com/item/pthread_detach

猜你喜欢

转载自blog.csdn.net/m0_46529816/article/details/114272139