操作系统---线程初识1

线程是什么呢?

在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。在linux下线程一般都被称为轻量级进程。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程至少都有一个执行线程。

创建一个线程:

功能:创建一个新的线程
原型
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;失败返回错误码
代码实现
环境linux下
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void *rout(void *arg) {
int i;
for( ; ; ) {
printf("I'am thread 1\n");
sleep(1);
    }
}
int main( void )
{
pthread_t tid;
int ret;
if ( (ret=pthread_create(&tid, NULL, rout, NULL)) != 0 ) {
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
int i;
for(; ; ) {
printf("I'am main thread\n");
sleep(1);
    }
}

进程与线程的区别:

先来张进程与线程的图片

(1)进程是资源竞争的基本单位,而线程是CPU调度的最小单元
(2)同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
(3)进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束。
(4)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的。
(5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源。
(6)线程有自己的私有数据,线程ID,一组寄存器、信号屏蔽字、优先调度级、私有栈、上下文(移动要注意此处的私有栈和上下文,很重要),而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志。

linux下的线程有何特点?

Linux的线程是通过进程来模拟的,也就是说Linux里的线程本质上就是进程。即Linux下的线程一般都是轻量级的进程。
Linux程序如果用pthread_create启动一个新的线程,实际上启动了两个轻量进程,第一个是管理线程,第二个才是真正做事情的线程。但是后续新创建的线程就不需要再创建管理线程了。
Linux这种用进程模拟线程的方式,和signal机制不一致,signal是发给进程的,但是在linux里,往一个进程发送signal,实际上只有一个线程处理这个signal。
Linux的线程机制是通过内核和库混合实现的,所以线程的实现在Linux的核心态和用户态都有执行,内核实现线程/进程的调度,libpthread库实现线程之间的同步。这也就是为什么多线程程序需要连接一个libpthread库的原因。
Linux下的线程被称为轻量级进程,在内核中都对应一个调度实体,也拥有自己的进程描述符(task_struct结构体)。

线程的优点:

创建一个新线程的代价要比创建一个新进程小得多
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
线程占用的资源要比进程少很多
能充分利用多处理器的可并行数量
在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

线程的缺点:

性能损失

一个很少被外部事件阻塞的计算密度型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。

健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

缺乏访问控制

进程是访问的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响

编程难度提高

编写与调试一个多线程程序比单线程程序困难的多

线程组:

多线程的进程,又被称为线程组,线程组内的每一个线程在内核之中都存在一个进程描述符(task_struct)与之对应。进程描述符结构体中的pid,表面上看对应的是进程ID,其实不然,它对应的是线程ID;进程描述符中的tgid,含义是Thread Group ID,该值对应的是用户层面的进程ID

现在所说的线程ID,不同于pthread_t 类型的线程ID,和进程ID一样,线程ID是pid_t类型的变量,而且是用来唯一标识线程的一个整型变量。

查看一个线程ID命令:

ps -eLf |head -1 && ps -eLf |grep a.out |grep -v grep
ps命令中-L选项,会显示如下的信息:
LWP:线程ID,即gettid()系统调用的返回值
NLWP:线程组内线程的个数

在线程中需要注意的是,线程和进程不一样,进程有父进程的概念,但在线程组里边,所有的线程都是对等关系。

Linux提供了gettid系统调用来返回其线程ID,可是glibc并没有将该系统调用封装起来,在开放接口来共程序员使用。如果确实需要获得线程ID,可以采用如下方法:
#include

/* 线程组ID等于线程ID,group_leader指向自身 */
p->tgid = p->pid;
p->group_leader = p;
INIT_LIST_HEAD(&p->thread_group);
至于线程组其他线程的ID则有内核负责分配,其线程组ID总是和主线程的线程组ID一致,无论是主线程直接创建线程,还是创建出来的线程再次创建线程,都是这样。
if ( clone_flags & CLONE_THREAD )
p->tgid = current->tgid;
if ( clone_flags & CLONE_THREAD ) {
P->group_lead = current->group_leader;
list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);
}

线程ID及进程地址空间布局

pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID不是一回事。前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程。
pthread_ create函数产生并标记在第一个参数指向的地址中的线程ID中,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
线程库NPTL提供了pthread_ self函数,可以获得线程自身的ID:
pthread_t pthread_self(void);

对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质上就是一个进程地址空间上的一个地址。

来张图来解释下:
线程结构图

之前在线程初识1中知晓了如何创建一个线程。
博客链接:https://mp.csdn.net/mdeditor/80150844

有开始就会有结束,终止线程的方式(只终止线程而不终止进程):

1、从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit
2、线程可以调用pthread_exit终止自己。
3、一个线程可以调用pthread_cancel终止同一进程找那个的另一个进程。

pthread_exit 函数

功能:线程终止
原型
void pthread_exit(void *value_ptr);
参数

value_ptr:value_ptr不要指向一个局部变量
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

pthread_cancel函数

功能:取消一个执行中的线程
原型
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值