为什么要引入线程?
由于进程是资源的拥有者,在创建、撤销、切换等操作中需要较大的时空开销,限制了并发性的进一步提升。
为了减少进程切换的开销,将进程的资源分配单元和调度单元这两个属性分开处理,即进程仍然是资源分配的基本单位,
但不作为一个调度的基本单元(很少调度或切换),将调度执行和切换的责任分配给一个“线程”。
这样做的好处不仅可以提高系统的并发性,还可以适应新的对称多处理器(SMP)环境的运行线程标识符 有什么用,充分发挥其性能。
2.线程和进程的关系
例如,
线程是程序中的一个函数线程标识符 有什么用,进程是整个程序;
程序包含多个函数,已知一个进程包含多个线程;
进程中的线程同时运行,所以进程的总数是线程的时间是执行线程的最长时间,而不是所有线程时间的总和
1.linux中线程的概念
线程是OS调度和分派的基本单元,包含在进程中,线程是指进程内的一个执行单元。它的特点是什么?它比过程轻。进程的实体是程序,线程的实体是函数,共享进程的资源。进程中的多个线程可以并发执行,并且可以独立调度,只需要很少的资源。 .
Linux 早期不支持内核级线程,所以用户态 glibc 库以用户态线程的形式支持多线程。
每个线程都有一个id,我们称之为utid(或tid)。
后来的Linux内核支持多线程,一个进程中的所有内核线程都属于同一个组,并且每个内核线程都有一个唯一的编号,我们给它命名为ktid(用来区分用户态tid),所以一个线程有两个编号, utid 和 ktid。
这里要强调的是,用户级线程遵循 POSIX 标准并且是可移植的。
可以看到在Linux中,用户级线程和内核级线程是一对一的模型。
1.1 个线程标识符
与进程 ID 类似,线程也有一个线程 ID 来指示其身份。但有趣的是,这两者之间还是有一点区别的:
进程 ID 在整个系统中是唯一的,而线程 ID 仅在单个进程的上下文中是唯一的。线程ID仅在单个进程的上下文中是唯一的,也就是说,其他进程可能会生成与该进程相同的线程ID)
进程 ID 是整数值,但线程 ID 不一定是整数值。它很可能是一个结构。 (进程ID是整数值,线程ID不仅是整数值,还是结构体)
进程 ID 可以很容易地打印,而线程 ID 则不容易打印。 “的意思。因为线程在大多数情况下是一个结构,所以需要有一个函数可以比较两个线程。
#inlcude <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2); // 判断两线程是否是同一个
可以看到pthread_equal()函数接受两个线程参数,如果是同一个线程,返回非零值,否则返回0.
另一方面,我们有时需要知道这个线程的ID,在很多情况下,需要下面的函数
#include
pthread_t pthread_self(void); // 返回自己的线程ID
2.linux中的线程创建2.1线程创建函数
Linux实现线程由两部分组成:
内核线程支持 + 用户模式库支持 (glibc)。
Linux下调用glibc库中的pthread_create()函数创建线程。可以通过 man pthread_create 查看它的使用情况:
NAME
pthread_create - create a new thread
SYNOPSIS
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
这个函数有四个参数,
第一个参数pthread_t *thread是线程id的地址,
第二个参数const pthread_attr_t *attr是线程的属性,这里传了一个NULL指针,
第三个参数void *(*start_routine)(void *)很重要,它是线程实体函数的名字,
第四个参数 void *arg) 是传递给这个函数的参数。
通常当一个程序被创建并成为一个进程时,它会从默认线程开始。因此,我们可以说每个进程至少有一个控制线程,并且一个进程可以使用以下函数 Thread 创建额外的线程:
#include
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg);
参数说明:
2.2线程函数示例
#include
#include
#include // for thread
#include
#include
pthread_t tid[2]; // 创建两个线程
void *doSomeThing(void *arg)
{
unsigned long i = 0;
pthread_t id = pthread_self();
if(pthread_equal(id, tid[0]))
{
printf("\n First thread processing\n"); // 第一个线程正在执行
}
else
{
printf("\n Second thread processing\n");
}
for(i=0; i<(0xFFFFFFFF); i++);
return NULL;
}
int main(void)
{
int i = 0;
int err;
while(i < 2)
{
err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
if(err != 0) printf("\n can't create thread:[%s]", strerror(err));
else printf("\n Thread created successfully\n");
i++;
}
sleep(5);
return 0;
}
有两点不太合理:
doSomeThing() 函数末尾的“return NULL”;
主函数“sleep()”在.
上面代码的解释:
pthread_create()函数用于创建两个线程;
两个线程的启动函数都是doSomeThing();
在doSomeThing()函数中,线程使用pthread_self()和pthread_equal()来确认是在执行线程1还是线程2;
doSomeThing() 函数中的 for 循环只是浪费一点时间。
2.3线程函数编译
需要在编译时添加线程相关代码——lpthread选项声明需要连接线程库,以便调用头文件pthread.h中声明的函数。
编译后应该如下图所示。
gcc threadExample.c -o threadExample1 -lpthread
这是因为pthread库不是Linux系统的默认库,所以在使用pthread_create()函数创建线程时要链接静态库libpthread.a。
:~/Documents/course_2610/lab11_thread$ ./threadExample1
Thread created successfully
The first thread processing
The second thread processing
Thread created successfully
3. 总结
1)什么是用户模式线程,由谁创建,谁管理这些线程?为什么说用户模式线程是可移植的?
2)谁管理内核级线程?
3)进程和线程共享哪些资源,哪些不能共享?
4)运行第6步的代码,分析程序的结果。你有什么灵感?
请登录后发表评论
注册
社交帐号登录