1)理解操作系统线程的概念和应用编程过程;
2)理解线程的同步概念和编程;
1)在Ubantu 或Fedora 环境使用fork 函数创建一对父子进程,分别输出各自的进程号和提示信息串。
2)在Ubantu 或Fedora 环境使用pthread_create 函数创建2 个线程A 和B。线程A 在屏幕上用while 循环顺序递增地输出1-1000 的自然数;线程B 在屏幕上用while 循环顺序递减地输出1000-1 之间的自然数。为避免输出太快,每隔0.5秒输出一个数。
3)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现(2)的功能。
4)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数和相关的同步函数,模拟实现“生产者-消费者”问题。“生产者-消费者”模拟实验的具体要求见后面附件。
5)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现“并发地画圆和画方”。圆的中心,半径,颜色,正方形的中心,边长,颜色等参数自己确定,合适就行。圆和正方形的边界上建议取720 个点。为直观展示绘制的过程,每个点绘制后睡眠0.2 秒~0.5 秒。
6)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现“文件拷贝小工具”。功能如下:1)具有一个编辑框,让用户任意指定源目录或文件,2)具有一个编辑框,让用户任意指定目的目录或文件;3)具有“开始拷贝”按钮;4)具有“停止拷贝”按钮5)具有显示拷贝进度的label,当为目录拷贝时以文件数来统计进度,当为文件拷贝时以字节数来统计进度。
①任务环境
Ubuntu版本:16.04 64位
Linux内核版本:4.5.0-29-generic
②编写在Linux新建一个空文档,命名为T2_1.c
并添加如下代码:
说明:
由 fork 创建的新进程被称为子进程。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。子进程可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。
fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置
③通过gcc T2_1.c命令进行编译
④编译产生a.out文件
⑤输入./a.out运行编译后的文件
未选题
①编写代码:
#include
#include
#include
//线程函数,输出0-1000或1000-0的自然数
DWORD WINAPI print1(LPVOID n) {
int num = (int)n;
int i = 0;
time_t lasttime;
while (i < num)
{
time(&lasttime);
while (1)
{
time_t nowtime;
time(&nowtime);
if (nowtime - lasttime >= 0.5)
{
printf("test1->%d\n", i);
i++;
break;
}
}
}
printf("\n");
return 0;
}
DWORD WINAPI print2(LPVOID n) {
int num = (int)n;
int i = 1000;
time_t lasttime;
while (i > num)
{
time(&lasttime);
while (1)
{
time_t nowtime;
time(&nowtime);
if (nowtime - lasttime >= 0.5)
{
printf("test2->%d\n", i);
i--;
break;
}
}
}
printf("\n");
return 0;
}
int main()
{
HANDLE hThread[2];
//创建线程,并调用函数打印输出
hThread[0] = CreateThread(NULL, 0, print1, (LPVOID)1000, 0, NULL);
hThread[1] = CreateThread(NULL, 0, print2, (LPVOID)1, 0, NULL);
//等待所有线程结束
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
return 0;
}
②运行结果:两个线程同时运行,交替输出
任务环境:Windows 10 x64 + Visual Studio 2017 Enterprise
代码实现:
#include
#include
#define N 100
#define TRUE 1
typedef int Semaphore;
Semaphore full = 0, Empty = N; //共享资源区满槽数目和空槽数目
int in = 0, out = 0; //缓冲区生产,消费数据指针
HANDLE mutex;
int ProducerThread[5];
int ConsumerThread[5];
int Buffer[N + 4]; //缓冲区
int produce_item() { //生产(随机数)
return (rand() % N + N) % N;
}
//插入资源
int insert_item(int item)
{
in %= N;
printf("生产到缓冲区槽: %d\n", in);
Buffer[in] = item;
return Buffer[in++];
}
//移出资源
int remove_item() {
out %= N;
printf(" 取走缓冲区槽 %d 的数\n", out);
return Buffer[out++];
}
void down(HANDLE handle) { //wait / P
WaitForSingleObject(handle, INFINITE);
}
void up(HANDLE handle) { //signal / V
ReleaseSemaphore(handle, 1, NULL);
}
DWORD WINAPI producer(LPVOID v) {
int item;
while (TRUE) {
item = produce_item();
if (Empty > 0) { //down(empty)
Empty--;
down(mutex); //down(mutex)
insert_item(item);
full++; //up(full)
up(mutex); //up(mutex)
}
Sleep(2000);
}
return 1;
}
DWORD WINAPI consumer(LPVOID v) {
int item;
while (TRUE) {
if (full > 0) { //down(full)
full--;
down(mutex); //down(mutex)
item = remove_item();
Empty++; //up(empty)
up(mutex); //up(mutex)
}
Sleep(2000);
}
return 1;
}
int main()
{
DWORD Tid;
mutex = CreateSemaphore( //创建互斥信号量mutex
NULL,
1,
1,
NULL
);
for (int i = 0; i<4; i++) {
ProducerThread[i] = i + 1;
CreateThread( //创建生产者线程
NULL, //不能被子线程继承
0, //默认堆栈大小
producer, //生产者函数
&ProducerThread[i], //传参
0, //创建后立即执行
&Tid //线程ID
);
ConsumerThread[i] = i + 1;
CreateThread(NULL, 0, consumer, &ConsumerThread[i], 0, &Tid); //创建消费者线程
}
Sleep(20000);
return 0;
}
任务环境:Windows 10 x64 + Visual Studio 2017 Enterprise
代码实现:
#include
#include
#include
CRITICAL_SECTION cs;//定义临界区全局变量
//画圆
DWORD WINAPI circle(LPVOID n)
{
EnterCriticalSection(&cs);
double m = 0;
int cx = 0, cy = 0;
for (cy = 10; cy >= -10; cy--) //表示图形的第y行,通过for循环打印所有行
{
m = 2.5*sqrt(100 - cy * cy); //用y作为自变量,根据弦长与纵坐标y的函数关系计算出此行上的弦 长的一半也就是两个星形符号之间的距离的一半,并用m表示。
for (cx = 1; cx < 50 - m; cx++) //以50个字符长度为基准根据弦长来确定每行左数第一个星形的位 置,此位置前全印空格
printf(" ");
printf(".");
Sleep(100);
for (; cx < 50 + m; cx++) //以50个字符宽度为基准来确定每行第二个星形的位置
printf(" ");
printf(".\n");
Sleep(100);
}
LeaveCriticalSection(&cs);
return 0;
}
//画方
DWORD WINAPI tangle(LPVOID n)
{
EnterCriticalSection(&cs);
int x = 0, y = 0;
for (y = 20; y >= 0; y--)
{
if (y == 20 || y == 0) {
for (x = 1; x <= 20; x++)
{
printf(" ");
printf(".");
Sleep(100);
printf(" ");
}
printf("\n");
}
else if (y != 20 || y != 0)
{
printf(" ");
printf(".");
Sleep(100);
printf(" ");
for (x = 2; x < 20; x++)
{
printf(" ");
printf(" ");
printf(" ");
}
printf(" ");
printf(".");
Sleep(100);
printf(" ");
printf("\n");
}
}
LeaveCriticalSection(&cs);
return 0;
}
int main()
{
//初始化临界区
InitializeCriticalSection(&cs);
HANDLE hThread[2];
//创建线程,并调用函数打印输出
hThread[0] = CreateThread(NULL, 0, circle, (LPVOID)0, 0, NULL);
hThread[1] = CreateThread(NULL, 0, tangle, (LPVOID)0, 0, NULL);
//等待所有线程结束
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
//删除临界区
DeleteCriticalSection(&cs);
getchar();
return 0;
}
未选题
父子进程成功创建,并输出了各自的进程号和提示信息串
未选题
两个线程同时运行,交替输出
结果如图所示
两个线程同时运行,实现了并发的画圆与画方
未选题
通过本次实验,我对操作系统线程的概念和生成过程、操作系统线程的同步概念有了更深刻的理解,掌握了如何在Linux与Windows下创建线程,对操作系统的功能与原理有了进一步的了解。