【Linux学习之系统编程】Linux线程基础

本文介绍了线程概念和线程控制相关知识。线程是进程内部的控制序列,是程序执行的最小单位,有优点也有缺点。还阐述了线程 ID 的相关内容,包括其在 Linux 中的实现和获取方法。在线程控制方面,讲解了线程的创建、终止、等待与分离等操作。

一. 线程概念

1.线程

典型的UNIX进程可以看成只有一个控制线程:一个进程在某个时刻只能做一件事

故,线程是一个进程内部的的控制序列

2.进程和线程

·进程是资源竞争的基本单位

·线程是程序执行的最小单位

·一个进程中的线程共享同一份资源,包括同一地址空间,文件买哦首付与表,每种信号的处理方式,当前工作目录,用户ID和组ID

·线程也有自己数据:线程ID, 一组寄存器, 栈,  调度优先级和策略, 信号屏蔽字, errno变量,以及线程的私有数据

3.线程的优点

·创建一个线程比创建一个进程的代价小许多

·与切换进程相比,线程之间的切换更方便

·线程比进程占用的资源少

·可以充分利用多处理器的并行操作

·线程之间有方便的通信机制,同一进程下,一个线程的数据可以直接被其他线程使用,快捷方便

·在等待慢速I/O操作结束时,程序可以执行其他的计算任务

·计算密集型应用,为了能再多处理器系统中运行,将计算分解到多个线程中实现

·I/O密集型应用为了提高性能,将I/O操作重叠,多个线程可以等待不同的I/O操作

4.线程的缺点

·性能损失,线程有额外的同步和调度的开销,但是可用的资源不变

·代码鲁棒性降低,线程之间可能会因为共享资源而造成不良的影响,导致代码的健壮性不强

·缺乏访问控制,进程是访问控制的基本粒度,线程调用某些OS函数会造成影响

·编程难度提高了(确实)

5.线程ID

·在Linux中,目前的线程实现是Native POSIX Thread  Libaray,简称NPTL.在这种实现下,线程又被称为轻量级进程,每一个用户态的线程,在内核中都有对应的一个调度实体,也拥有自己的进程描述符

·没有线程之前,一个进程有一个进程ID,但是有了线程之后,一个用户进程管辖N个用户态线程,每个线程作为独立的调度单位在内核中都有自己的进程描述符(task_struct),进程和内核的描述符变为了1:N的关系

由于以上原因,Linux引入了线程组的概念

struct task_struct {    
    ...    
    pid_t pid;    
    pid_t tgid;    
    ...    
    struct task_struct *group_leader;    
    ...    
    struct list_head thread_group;    
    ... 
};

·多线程的进程又被称为进程组,线程组内的每一个线程在内核之中都存在一个进程描述符与之对应.

   用户态     系统调用         内核进程描述符对应的结构
   线程IDpid_t gettid(void)                    pid_t pid
   进程IDpid_t getpid(void)                     pit_t tid 

所以,进程描述符中的ID其实是Thread Group ID,意思是用户层面的ID,线程ID是一个进程ID 

如果需要查看线程ID,可以通过ps命令,加上-L选项,会出现如下信息:

·LWP:线程ID,调用gettid()系统调用时的返回值

·NLWP:线程组内线程的个数

Linux提供了gettid系统调用来返回其线程ID,可以这个函数没有被封装起来,并没有提供接口给我们

所以我们获取线程ID时使用如下方法:

#include <sys/syscall.h>
pid_t tid;
tid = syscall(SYS_gettid);

在Linux中,存在一个线程ID和线程组ID(进程)相等,该线程就是想线程组的主线程,主线程在内核中被称为group_leader

内核在创建第一个线程时,会把线程组的ID设置成第一个线程的ID,group_leader则指向自身

线程组其他的线程ID由内核分配,其线程组ID总是和主线程ID相同

注意,线程和进程不一样,进程有父子进程的概念,而一个线程组中所有的线程都是对等的关系

6.线程ID和进程地址空间

·线程控制时,调用pthread_create创建一个线程,其线程ID存储在这个函数的第一个参数之中. 这种ID和前面说的线程ID不一样

·上面提到的线程ID是属于进程调度的范畴,因为线程是轻量级进程,是操作系统调度器的最小的单位,所以需要一个数值来表示该唯一的线程,所以gettid获取的是内核中的线程ID,pthread_self获取的是POSIX中定义的线程ID,这种ID只是用来区分某个进程中不同的线程,当一个线程退出后,新创建的线程可以用来复用原来的ID

·线程库实际上由两个部分组成:内核的线程支持和用户态的线程支持,Linux早期内核不支持线程的时候glibc就在库中支持多线程了,其实就是用户态多线程.

·Linux上的线程实现就是在内核支持的基础上以POSIXthread的方式对外封装了接口,所以才会有两个线程ID

pthread_self函数:

pthread_t pthread_self(void);

pthread_t的类型取决于实现,对于Linux目前实现的NPTL来说,pthread_t类型的线程ID本质上是一个进程地址空间上的地址

二.线程控制

1.创建线程

与线程有关的函数构成一个完整的序列,使用时引入头文件pthread,编译时要加上-lthread链接静态库

//函数原型    
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
                   void* (*star t_routine)(void*), void *arg); 

/* 
**参数    
**thread:返回线程ID    
**attr:设置线程的属性,attr为NULL表⽰示使⽤用默认属性    
**start_routine:是个函数地址,线程启动后要执⾏行的函数    
**arg:传给线程启动函数的参数 返回值:成功返回0;失败返回错误码
**返回值:成功返回0;失败返回错误码
*/

pthreads函数出错时不会设置errno变量,所以对于线程的错误,建议通过返回值判定

2.线程终止

终止线程而不终止进程时,有三种方法:

·从线程函数return,对于主线程而言,调用return相当于调用exit

·线程调用pthread_exit终止自己

·同组的其他线程通过pthread_cancel可以z终止当前线程

pthread_exit函数:

功能:线程终⽌止 
原型   
void pthread_exit(void *value_ptr); 
参数    
value_ptr:value_ptr不要指向⼀一个局部变量。 
返回值:⽆无返回值,跟进程⼀一样,线程结束的时候⽆无法返回到它的调⽤用者(⾃自⾝身)

pthread_cancel函数:

功能:取消⼀一个执⾏行中的线程 

原型    
int pthread_cancel(pthread_t thread); 

参数 thread:线程ID 返回值:成功返回0;失败返回错误码

3.线程等待与分离

·线程等待

为什么需要线程等待?已经退出的线程,其空间没有被释放,创建新的线程时不会复用刚才退出线程的空间

pthread_join函数:

功能:等待线程结束 

原型    
int pthread_join(pthread_t thread, void **value_ptr); 

参数    
thread:线程ID    
value_ptr:它指向⼀一个指针,后者指向线程的返回值 返回值:成功返回0;失败返回错误码

调用这个pthread_join函数的线程将阻塞的等待id为thread的线程终止,thread以不同的方式终止,得到的终止方式也不同

·如果是return返回,value_ptr存放的是线程函数的返回值

·如果被别的线程调用pthread_cancel函数终止掉,value_ptr里存放的是PTHREAD_CANCELED

·如果thread线程调用pthread_exit终止,则value_ptr存放的是传给pthread_exit的参数

如果对线程的返回状态不感兴趣,则可以把value_ptr置为NULL

·线程分离

默认情况下,新创建的线程是joinable(可结合)的,线程退出后,需要调用pthread_join函数来进行线程等待,否则无法释放资源

如果不关心线程的返回状态,则pthread_join函数并没有什么用,反而浪费资源(有个线程在阻塞式的等待),所以就用到了线程分离

pthread_detach函数:

int pthread_detach(pthread_t thread);

举个栗子:

一定要先等一会儿,确保新创建的线程执行了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值