2024年5月25日发(作者:)

《操作系统》实验报告

实验序号:4 实验项目名称:线程创建和数据传递

学 号

实验地点

姓 名

指导教师

ZRZ

李远敏

专业、班

实验时间

18计卓1班

2020.4.24

一、实验目的及要求

1.加深对线程概念的理解,明确进程和线程的区别。

2.进一步认识线程并发执行的实质。

3.学会利用pthread库,进行线程创建和线程间数据传递

二、实验设备(环境)及要求

操作系统环境

2.搭建Ubuntu操作系统下的C语言编程环境。

三、实验内容与步骤

1.线程的创建:

(1)编程实现在主进程中通过线程创建pthread_create()函数来创建新的线程。

(2)在主线程和子线程中要通过getpid()及pthread _self()获取对应的进程号和线程号并打

印输出

2.线程间参数传递:

(1)阅读下面主线程与子线程间传参数(多个参数值)的程序代码并输入调试并输出结

果;

(2)按下面要求编写子线程间参数传递程序

a.程序功能:

实现在两个子线程通过全局变量传递数据:即在一个子线程中对全局变量的成员进行赋

值,在另一个子线程中对全局变量的成员进行取值并显示

b.编程要求:

首先要定义一个全局结构类型和对应的变量,定义两个Pthread_t类型变量来保存两个

子线程的id;要定义一个主函数和两个用于创建子线程的对应函数

在主函数中创建两个函数对应的子线程并对创建是否成功进行判断,创建成功后,等待

显示子线程结束进程才结束。

在赋值子线程对应函数内要打印进入该子进程的特殊信息,分别对全局变量中相应成员

赋值。

在显示子线程对应函数内要先等待赋值子线程运行完该线程才开始,打印进入该子进程

的特殊信息,并取出全局变量中每个成员的值并显示。

3.接收子线程的返回值:

(1)阅读下面接收创建子线程的返回值-简单值的程序代码并输入调试并输出结果;针对

下列问题进行说明或修改:

a.程序运行结果ret= 是一个不确定的值,试说明原因

b.将程序中int a=20语句调换到红颜色位置,结果如何

c. 将程序中int a=20 int*p=&a;语句置换成蓝颜色语句,结果又如何?

(2)阅读下面接收创建子线程返回一个复杂的数据结构变量程序代码并输入、调试、输

出结果;针对下列问题进行说明或修改:

将temp定义和赋值变为create()函数内定义的局部变量,应如何修改程序对结果又

什么影响并说明原因?

四、实验结果与数据处理

1.线程的创建

(1)实验结果:

图1:进程的创建一代码

图2:程序运行结果

(2)结果分析:

运行结果:我们可以看到主线程会先打印出相应的ID,然后再是子线程打印自己的ID

原因:当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻

运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果

你需要再创建线程,那么创建的线程就是这个主线程的子线程。每个进程至少都有一个主线程,

所以没有干预的情况下,主线程都是优先于子线程运行的。

所以在输出ID的时候,主线程会比子线程早输出自己的ID。

(3)思考:

a.通过以上实验线程执行与函数调用的不同,线程与进程的不同

答:线程是程序执行部分,是操作系统的划分。函数是编程中的概念,是功能模块的划分。

b.如何控制主线程与子线程的执行顺序

答:我们可以使用sleep函数来对线程进行挂起的操作,从而来控制主线程与子线程的执

行顺序。

图3:修改后的代码

图4:修改后的代码运行结果

总结:

①线程被创建后,并不能保证那个线程先执行,新创建的线程和调用线程的执行顺序不确

定,由操作系统进行调度,注意:编译时要连接库libpthread;就是编译的时候要加 -lpthread

②在C程序中, main(int argc, char **argv) 就是一个主线程。我们可以在主线程中做任何

普通线程可以做的事情,但它和一般的线程有有一个很大的区别:主线程返回或者运行结束时

会导致进程的结束,而进程的结束会导致进程中所有线程的结束。为了不让主线程结束所有的

线程,可以用sleep函数挂起线程

2.线程间参数传递

(1)实验结果:

1.输入示例代码调试并输出结果

图5:线程间参数传递代码

图6:程序运行结果

2. 按下面要求编写子线程间参数传递程序

a.程序功能:

实现在两个子线程通过全局变量传递数据:即在一个子线程中对全局变量的成员进行赋

值,在另一个子线程中对全局变量的成员进行取值并显示

b.编程要求:

首先要定义一个全局结构类型和对应的变量,定义两个Pthread_t类型变量来保存两个

子线程的id;要定义一个主函数和两个用于创建子线程的对应函数

在主函数中创建两个函数对应的子线程并对创建是否成功进行判断,创建成功后,等待

显示子线程结束进程才结束。

在赋值子线程对应函数内要打印进入该子进程的特殊信息,分别对全局变量中相应成员

赋值。

在显示子线程对应函数内要先等待赋值子线程运行完该线程才开始,打印进入该子进程

的特殊信息,并取出全局变量中每个成员的值并显示。

图7:线程间参数传递新代码

图8:程序运行结果

3.接收子线程的返回值

(1)实验结果:

图9:返回简单数据类型程序代码

图10:返回简单数据类型程序运行结果

图11:返回复杂数据类型程序代码

图12:返回复杂数据类型程序运行结果

(2)结果分析:

a. 程序运行结果ret= 是一个不确定的值,试说明原因

答:因为我们在子线程调用的函数中运用了int a=20; int *p=&a的办法,这样会导致ret

的值不确定,因为这时候的p是一个局部变量,当函数执行完毕后就会被释放掉,而释放

掉以后就会造成指针悬挂的问题,致使ret的值不确定,造成内存泄漏。所以我们要针对

这点的话,就要采用全局变量的方法或者是动态释放内存的方法来解决。

b. 将程序中int a=20语句调换到红颜色位置,结果如何

结果为:

图13:程序运行结果

答:ret的值变为了20

c. 将程序中int a=20 int*p=&a;语句置换成蓝颜色语句,结果又如何?

图14:程序运行结果

答:ret的值变为了10

(3)总结:

我们在接受子线程的返回值时,要注意设置为全局变量或者采用动态内存分配的方法才不

会影响到系统的其他功能,保证程序运行过程无误且顺利。

同时我们使用到了pthread_exit() 和pthread_join() 来控制线程,要注意的是pthread_exit()

终止线程,但不释放非分离线程的内存空间;当调用 pthread_join() 时,当前线程会处于阻塞

状态,直到被调用的线程结束后,当前线程才会重新开始执行。当 pthread_join() 函数返回后,

被调用线程才算真正意义上的结束,它的内存空间也会被释放(如果被调用线程是非分离的)。

这里有三点需要注意:①被释放的内存空间仅仅是系统空间,你必须手动清除程序分配的空间,

比如 malloc() 分配的空间。②一个线程只能被一个线程所连接。③被连接的线程必须是非分离

的,否则连接会出错。

同时我们深入了解pthread_join() 后可以得知,在Linux中一个线程只可能是可连接或者

可分离。我们创建一个线程的时候,线程默认是可连接。可连接和可分离的线程有以下的区别:

线程类型

可连接的线程

可分离的线程

说明

能够被其他线程回收或杀死,在其被杀死前,内存空间不会自动被释

不能被其他线程回收或杀死,其内存空间在它终止时由系统自动释放

我们可以看到,对于可连接的线程而而言,它不会自动释放其内存空间。因此对于这类线

程,我们必须要配合使用 pthread_join() 函数。而对于可分离的函数,我们就不能使

用 pthread_join() 函数。

五、分析与讨论

通过这一次的实验,我对线程的控制和管理有了更多的了解,也学会了如何创建线程和

线程间的参数访问,也懂得了线程函数返回简单数据类型的值和返回复杂数据类型的值的原理

和所用到的函数,明白了pthread_create()、getpid()、pthread_self()、pthread_exit() 、

pthread_join()、sleep()等等函数,对于线程的整个运行过程和运行方式有了更加深入地体会,

让我对linux操作系统的学习更加得心应手了。

六、教师评语

签名:

日期:

成绩