【北邮大三上操作系统】第二组 线程创建与管理与线程通信

发布于 2023-07-21  1395 次阅读


第二组 线程创建与管理与线程通信

1 题目

1. 线程创建与管理

查阅 Linux 和 Pthread 线程库相关资料,参照相关示例程序,设计父进程/线程和子线程的业务处理逻辑;利用 pthread_createpthread_exitpthread_cancelpthread_joinpthread_self
等线程管理函数,创建和管理线程,观察父子进程/线程的结构和并发行为,掌握等待、退出、撤销等线程控制方法。

要求:

  • 在一个进程内创建多个子线程(如不少于 3 个子线程),父子子线程具有各自不同的业务处理逻辑,子线程执行自身特定的线程函数。父子线程间通过pthread_join()函数实现同步和资源释放;

  • 一个进程内的多个子线程分别以不同方式终止或退出执行,如通过 pthread_exit()return 自己主动终止,或被被其它线程通过 pthread_cancel 被动终止,观察对比以不同方式退出执行的子线程的行为差异;

  • 在创建的父子进程/线程代码中的不同位置处增加数十到数百毫秒的随机延迟(使用sleep()),使得进程和线程的执行横跨多个时间片,保证线程执行时间不少于 3 个时间片。

2. 线程通信

属于同一个进程中的多个线程使用相同的地址空间,共享大部分数据,一个线程的数据可以直接为其它线程所用,这些线程相互间可以方便快捷地利用共享数据结构进行通信。

编写程序,创建线程,实现主线程与子线程间、子线程相互间通过共享数据类型,如整型变量、字符串、结构体等传递信息,进行通信。

2 实验目的

  • 理解线程的概念
  • 掌握在 linux 下创建线程的基本方法
  • 掌握LINUX下线程通信的方法

3 实验内容

3.1 实验一

POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。因为pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显式链接该库,POSIX标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。

3.2 实验二

线程间通信分为两种情况:

  1. 分属于不同进程的线程间的通信。可以采用进程间通信机制,如消息队列;
  2. 位于同一进程内的不同线程间的通信。此时,线程可以通过共享的进程全局数据结构进行快速通信,但需要考虑采用合适的同步互斥机制,以保证访问结果的正确,这类似于进程间的同步与互斥。

4 实验设计原理

4.1 实验一

函数 传入参数 返回值 作用
pthread_create (pthread_t *thread,pthread_attr_t*attr,void(start_routine)(void ), void\arg)
thread:指向所创建线程的标识符的指针,线程创建成功时,返回被创建线程的 ID。
attr:用于指定被创建线程的属性,NULL 表示使用默认属性。
start_routine: 函数指针,指向线程创建后要调用的函数,是一个以指向 void的指针作为参数和返回值的函数指针,这个被线程调用的函数也被称为线程函数。
arg:指向传递给线程函数的参数
创建成功:0创建失败:返回错误码 创建线程
pthread_exit retval:线程退出时的返回值,可被 pthread_join()等其它函数获取 终止进程内特定线程
pthread_join (pthread_t thread,void **thread_return)
thread:等待退出的线程 ID。
thread_return:用于定义的指针,存储被等待线程结束时的返回值(不为 NULL 时)
等待成功:0。等待失败:返回错误码 实现线程同步和资源释放。将当前进程挂起来等待线程的结束。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回

分别创建三个子线程,一个进程内的多个子线程分别以不同方式终止或退出执行,通过 pthread_exit()return 自己主动终止,或被其它线程通过pthread_cancel 被动终止

4.2 实验二

线程间通讯可以通过pthread_create函数中的参数传递,或者是通过全局变量来进行访问修改参数(如果有多个线程对参数进行访问需要加锁)

分别写了三个进程,其中sendChar线程接收字符串,sendStruct线程接收结构体变量,getData线程访问全局变量并进行修改。

由于传递过来的参数是void*类型,需要先强制转换为指定的数据类型

5 实验步骤

5.1 实验一

在vim文本编译器里编写代码

thread.c部分代码

编译执行

gcc thread_manage.c -o thread_manage -lpthread

运行程序

5.2 实验二

在vim文本编译器里编写代码

ITC.c部分代码

编译执行

gcc ITC.c -o ITC -lpthread

运行结果

6 实验结果及分析

6.1 实验一

  1. 使用pthread_exit()函数退出的线程,线程返回值为pthread_exit指定的参数;
  2. 使用return 自然退出的线程,使用thread_join查看线程返回值则为return结果
  3. 在主线程里调用pthread_cancel可以强制中止线程的结束

6.2 实验二

实验分析:

  1. 数据通过pthread_create函数的参数从主线程传递到创建的新线程中,无论该数据是字符串还是用户自定义的结构体变量。

  2. 使用全局变量进行数据传递,在新创建的线程中修改全局变量后,主线程也可以得到改变后的全局变量的值。

7 程序代码

7.1 实验一

#include<pthread.h>
#include<stdio.h>
#include <stdio.h>
#include <sys/select.h>
static void sleep_ms(unsigned int secs)//实现ms级别的sleep
{
    struct timeval tval;
    tval.tv_sec = secs / 1000;
    tval.tv_usec = (secs * 1000) % 1000000;
    select(0, NULL, NULL, NULL, &tval);
}
void* thread_1(void* arg) {//exit退出
    int i;
    for (i = 0; i < 4; i++)
    {
        printf("now running thread [1] ID:%d\n", (unsigned int)pthread_self());
        sleep_ms(50);
    }
    pthread_exit((void*)1);
}

void* thread_2(void* arg) {//正常执行完毕退出
    int j;
    for (j = 0; j < 4; j++)
    {
        sleep_ms(345);
        printf("now running thread [2] ID:%d\n", (unsigned int)pthread_self());

    }
    return 2;
}
void* thread_3(void* arg) {//cancel
    int k;
    for (k = 0; k < 4; k++)
    {
        printf("now running thread [3] ID:%d\n", (unsigned int)pthread_self());
        sleep_ms(789);
    }
}

int main() {
    int res = 0;
    void* code1, * code2, * code3;
    pthread_t thread1, thread2, thread3;
    /*线程1pthread_exit*/
    res = pthread_create(&thread1, NULL, (void*)thread_1, NULL);
    if (res != 0) {
        printf("create thread 1 failed\n");
        return -1;
    }
    res = pthread_join(thread1, &code1);
    if (res) {
        printf("thread 1 is not exit\n ");
        return -2;
    }
    printf("The exit of thread 1 is %d\n", (int)code1);

    /*线程2 正常return*/
    res = pthread_create(&thread2, NULL, (void*)thread_2, NULL);
    if (res != 0) {
        printf("create thread 2 failed\n");
        return -1;
    }
    pthread_join(thread2, &code2);
    if (res) {
        printf("thread 2 is not exit\n ");
        return -2;
    }
    printf("The exit of thread 2 is %d\n", (int)code2);

    /*线程3 cancel被迫中止*/
    res = pthread_create(&thread3, NULL, (void*)thread_3, NULL);
    if (res != 0) {
        printf("create thread 2 failed\n");
        return -1;
    }
    sleep_ms(50);
    res = pthread_cancel(thread3);
    if (res != 0) {
        printf("fail to cancel thread3\n");
    }
    res = pthread_join(thread3, &code3);
    if (res != 0) {
        printf("fail to join thread3\n");
        return 0;
    }
    if (code3 == PTHREAD_CANCELED) {
        printf("thread3 is canceled\n");
        printf("The exit of thread 3 is %d\n", (int)code3);
    }
    else {
        printf("error\n");
    }

    return 0;
}

7.2 实验二

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
static int a = 5;
typedef struct member
{
    int a;
    char* s;
}member;
void* sendChar(char* arg)
{
    char* str;
    str = arg;
    printf("=========\n");
    printf("now running thread sendChar\n");
    printf("The parameter passed from main is %s\n", str);
    return (void*)0;
}
void* sendStruct(void* arg)
{
    member* recv;
    member* send;
    recv = (member*)arg;
    printf("=========\n");
    printf("now running thread sendStruct\n");
    printf("menber->a = %d\n", recv->a);
    printf("menber->s = %s\n", recv->s);
    send = (member*)malloc(sizeof(member));
    send->a = recv->a * 2;
    send->s = "Jack";
    return (void*)send;
}
void* getData(void* arg)
{
    printf("=========\n");
    printf("now running thread getData\n");
    printf("New pthread...\n");
    printf("a = %d\n", a);
    a = 3;
    return (void*)0;
}
int main()
{
    int error;
    pthread_t id1, id2, id3;
    char* str1 = "Hello!";
    char* attr = str1;
    error = pthread_create(&id1, NULL, sendChar, (void*)attr);
    if (error != 0)
    {
        printf("This pthread is not created!\n");
        return -1;
    }
    sleep(1);
    printf("pthread is created..\n");

    member* p;
    void* get;
    p = (member*)malloc(sizeof(member));
    p->a = 1;
    p->s = "Robben!";
    error = pthread_create(&id2, NULL, sendStruct, (void*)p);
    if (error)
    {
        printf("pthread is not created!\n");
        return -1;
    }
    sleep(1);
    printf("pthread is created!\n");
    pthread_join(id2, &get);
    printf("member->a=%d\n", ((member*)get)->a);
    printf("member->s=%s\n", ((member*)get)->s);
    free(p);
    p = NULL;

    error = pthread_create(&id3, NULL, getData, NULL);
    if (error != 0)
    {
        printf("new thread is not created!\n");
        return -1;
    }
    sleep(1);
    printf("New thread is created...\n");
    printf("the data change into %d\n", a);
    return 0;
}