操原上机作业(二)

一、实验目的:
(1)理解操作系统线程的概念和应用编程过程;
(2)理解线程的同步概念和编程;
二、实验内容:

(1)在Ubantu 或Fedora 环境使用fork函数创建一对父子进程,分别输出各自的进程号和提示信息串。

相关参考:

fork 创建的新进程被称为子进程。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。子进程可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。
fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置

参考博客:
1. Linux环境下编程(一)——进程fork()的使用
2. fork出的子进程和父进程
3. 从一道面试题谈linux下fork的运行机制
4. Linux系统编程(二) —— 多进程编程

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>

void main()
{

    pid_t pid;

    fprintf(stdout, "I am the first process, my pid is %d, my parent pid is %d\n", getpid(), getppid());

    pid = fork();
    if (0 == pid)
        fprintf(stdout, "I am the child procress, my pid value is %d, but my real pid is %d, my parent pid is %d\n", pid, getpid(), getppid());
    else
        fprintf(stdout, "I am the parent process, my child pid is %d, my pid is %d, my parent pid is %d\n", pid, getpid(), getppid());
} 

(2)在Ubantu 或Fedora 环境使用pthread_create 函数创建2 个线程A 和B。线程A 在屏幕上用while 循环顺序递增地输出1-1000 的自然数;线程B在屏幕上用while 循环顺序递减地输出1000-1 之间的自然数。为避免输出太快,每隔0.5秒输出一个数。

相关参考:
头文件: #include<pthread.h>

pthread_create函数

函数声明:

int pthread_create(
            pthread_t *__restrict__  __newthread, 
            const pthread_attr_t *__restrict__  __attr,  
            void *(*__start_routine)(void *),  
            void *__restrict__ __arg
        );

参数:

  • 第一个参数为指向线程标识符的指针
  • 第二个参数用来设置线程属性。
  • 第三个参数是线程运行函数的起始地址。
  • 最后一个参数是运行函数的参数。

返回值: 若成功则返回0,否则返回出错编号

注意: 在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库

pthread_join函数

函数简介: 函数pthread_join用来等待一个线程的结束。

函数声明:

int pthread_join(
        pthread_t __th,  
        void **__thread_return
    );

参数:

  • 第一个参数为被等待的线程标识符
  • 第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。

注意: 这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。如果执行成功,将返回0,如果失败则返回一个错误号。

参考博客: linux创建线程之pthread_create

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

//打印 1~1000
void *print1()
{
    int  i = 0;
    while (i <= 1000)
    {
        printf("print1-> %d\n", i);
        i++;
    }
}

//打印1000~1
void *print2()
{
    int i = 1000;
    while (i > 0)
    {
        printf("print2-> %d\n", i);
        i--;
    }
}

int main()
{
    pthread_t tips1;
    pthread_t tips2;
    int hThread1 = pthread_create(&tips1, NULL, print1, NULL);
    int hThread2 = pthread_create(&tips2, NULL, print2, NULL);
    if (hThread1 != 0)
    {
        printf("hThread1 err");
    }
    else if (hThread2 != 0)
    {
        printf("hThread2 err!");
    }
    void *result1;
    void *result2;

    pthread_join(tips1, &result1);
    pthread_join(tips2, &result2);
    return 0;
} 

(3)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)
调用CreateThread 函数实现(2)的功能。

相关参考:

CreateThread函数:

    HANDLE CreateThread(  
                        LPSECURITY_ATTRIBUTES lpThreadAttributes,  
                        DWORD dwStackSize,  
                        LPTHREAD_START_ROUTINE lpStartAddress,  
                        LPVOID lpParameter,  
                        DWORD dwCreationFlags,  
                        LPDWORD lpThreadID  
                       );  

参数的含义如下:

  • lpThreadAttrivutes: 指向SECURITY_ATTRIBUTES的指针,用于定义新线程的安全属性,一般设置成NULL;
  • dwStackSize: 分配以字节数表示的线程堆栈的大小,默认值是0;
  • lpStartAddress: 指向一个线程函数地址。每个线程都有自己的线程函数,线程函数是线程具体的执行代码;
  • lpParameter: 传递给线程函数的参数;
  • dwCreationFlags: 表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;
  • lpThreadID: 返回新创建的线程的ID编号;

参考博客:
1. 使用CreateThread函数创建线程
2. 采用CreateThread()创建多线程程序
3. 多线程CreateThread函数的用法及注意事项

#include<stdio.h>
#include<Windows.h>
#include<time.h>

//线程函数,输出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 (true)
    {
        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;
    while (i > num)
    {
        printf("test2->%d\n", i);
        i--;
        Sleep(500);
    }
    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;
}

这里写图片描述


(4)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)
调用CreateThread 函数和相关的同步函数,模拟实现“生产者-消费者”问题

参考: Windows 用信号量实现生产者-消费者模型

法一:

#include <Windows.h>
#include <stdio.h>


#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;
}

法二:

#include <Windows.h>
#include <stdio.h>
#define N 100
#define TRUE 1
typedef int Semaphore;
Semaphore mutex = 1;           //互斥信号量
Semaphore full = 0, Empty = N; //临界区满槽数目和空槽数目
int in = 0, out = 0;           //缓冲区生产,消费数据指针
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++];
}

DWORD WINAPI producer(LPVOID v) {

    int item;

    while (TRUE) {

        item = produce_item();     //生产物品
        Empty--;                   //P(Empty)
        if (Empty < 0)              //没有空槽可以添加数据
            Empty++;               //还原Empty,继续循环等待
        else if (mutex > 0) {       //否则如果mutex = 1,临界区未被访问
            mutex--;               //加锁
            insert_item(item);     //往临界区填入数据
            full++;                //满槽数加1
            mutex++;               //释放锁
        }
        Sleep(2000);
    }
    return 1;
}

DWORD WINAPI consumer(LPVOID v) {

    int item;

    while (TRUE) {

        full--;                   //P(full)
        if (full < 0)              //如果没有满槽,无法消费
            full++;               //还原full,继续等待
        else if (mutex > 0) {      //否则如果mutex = 1,临界区未被访问
            mutex--;              //加锁
            item = remove_item(); //将数据移出临界区
            Empty++;              //空槽数目加1
            mutex++;              //释放锁
        }

        Sleep(2000);
    }
    return 1;
}

int main()
{
    DWORD Tid;

    for (int i = 0; i<4; i++) {
        ProducerThread[i] = i + 1;
        CreateThread(NULL, 0, producer, 0, 0, &Tid);
        ConsumerThread[i] = i + 1;
        CreateThread(NULL, 0, consumer, 0, 0, &Tid);
    }

    Sleep(20000);
    return 0;
}

这里写图片描述


(5)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)
调用CreateThread 函数实现“并发地画圆和画方”。圆的中心,半径,颜色,正
方形的中心,边长,颜色等参数自己确定,合适就行。圆和正方形的边界上建议
取720 个点。为直观展示绘制的过程,每个点绘制后睡眠0.2 秒~0.5秒。
(注: 下面代码没有实现同步打印,而是使用了临界区)

#include<stdio.h>
#include<math.h>
#include<Windows.h>

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;
}

这里写图片描述


(6)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)
调用CreateThread 函数实现“文件拷贝小工具”。功能如下:1)具有一个编辑框,
让用户任意指定源目录或文件,2)具有一个编辑框,让用户任意指定目的目录
或文件;3)具有“开始拷贝”按钮;4)具有“停止拷贝”按钮5)具有显示拷
贝进度的label,当为目录拷贝时以文件数来统计进度,当为文件拷贝时以字节
数来统计进度

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值