当前位置: 首页 > news >正文

实验二 多线程编程实验

一、实验目的

1、掌握线程的概念,明确线程和进程的区别。

2、学习Linux下线程创建方法及编程。

3、了解线程的应用特点。

4、掌握用锁机制访问临界区实现互斥的方法。

5、掌握用信号量访问临界区实现互斥的方法。

6、掌握线程下用信号量实现同步操作的方法。

二、实验内容

1、运行下列程序,给出执行结果,并分析运行结果。(3分)

1) #include <stdio.h>

#include <pthread.h>

#include <unistd.h>

// 打印函数(在屏幕上显示字符串)

void printer(char *str){

        while(*str!='\0')

        {   putchar(*str); 

            fflush(stdout);

            str++;

            sleep(1);

        }

        printf("\n");

}

// 线程一

void *thread_fun_1(void *arg)

{

        char *str = "hello";

        printer(str); //调用打印函数

}

// 线程二

void *thread_fun_2(void *arg)

{

        char *str = "world";

        printer(str); //调用打印函数

}

int main(void)

{

        pthread_t tid1, tid2;

        // 创建 2 个线程

        pthread_create(&tid1, NULL, thread_fun_1, NULL);

        pthread_create(&tid2, NULL, thread_fun_2, NULL);

        // 等待线程结束,回收其资源

        pthread_join(tid1, NULL);

        pthread_join(tid2, NULL);

        return 0;

}

编译及执行过程和运行结果截屏:

结果分析:

线程一和线程二,它们分别打印"hello"和"world"字符串。主线程使用pthread_join函数等待两个线程结束。

由于并发执行的原因,线程一和线程二的打印操作会交替进行,输出的结果可能是"howorldello"、"howerlllod"等不确定的顺序。

每个字符之间有1秒的延迟,所以输出的结果中每个字符之间会有间隔。最后输出一个换行符,每次执行的结果都会在新的一行显示。

  1. #include <stdio.h>

#include <pthread.h>

#include <unistd.h>

pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;//定义并初始化锁

//打印函数(在屏幕上显示字符串)

void printer(char *str)

{

        pthread_mutex_lock(&mutex_x);//上锁

        while(*str!='\0')

        {

            putchar(*str); 

            fflush(stdout);

            str++;

            sleep(1);

        }

        printf("\n");

        pthread_mutex_unlock(&mutex_x);//解锁

}

// 线程一

void *thread_fun_1(void *arg)

{

        char *str = "hello";

        printer(str); //调用打印函数

}

// 线程二

void *thread_fun_2(void *arg)

{

        char *str = "world";

        printer(str); //调用打印函数

}

int main(void)

{

        pthread_t tid1, tid2;

        // 创建 2 个线程

        pthread_create(&tid1, NULL, thread_fun_1, NULL);

        pthread_create(&tid2, NULL, thread_fun_2, NULL);

        // 等待线程结束,回收其资源

        pthread_join(tid1, NULL);

        pthread_join(tid2, NULL);

pthread_mutex_destroy(&mutex_x); //销毁互斥锁

        return 0;

}

编译及执行过程和运行结果截屏:

结果分析:

在程序中创建了两个线程,分别执行thread_fun_1和thread_fun_2函数。这两个函数都调用了printer函数,每次调用时都会上锁mutex_x,然后通过putchar函数逐个打印字符串中的字符,并休眠1秒钟。

由于两个线程同时运行,打印函数的执行是交替进行的。线程一先执行,打印"hello",然后释放锁,线程二获取到锁,打印"world",最后两个线程结束。

所以最终的输出结果为"hello world"。

  1. #include <stdio.h>

#include <pthread.h>

#include <unistd.h>

#include <semaphore.h>

sem_t semA; //声明一个名为semA的信号量变量

//打印函数(在屏幕上显示字符串)

void printer(char *str)

{

        sem_wait(&semA);//申请信号量(P操作)

        while(*str!='\0')

        {

            putchar(*str); 

            fflush(stdout);

            str++;

            sleep(1);

        }

        printf("\n");

        sem_post(&semA);//释放信号量(V操作)        

}

// 线程一

void *thread_fun_1(void *arg)

{

        char *str = "hello";

        printer(str); //调用打印函数}

// 线程二

void *thread_fun_2(void *arg)

{

        char *str = "world";

        printer(str); //调用打印函数}

int main(void)

{

        pthread_t tid1, tid2;

        if(sem_init(&semA, 0, 1))   //初始化信号量的值为1(二元信号量)

            printf("error sem_init!\n");

        // 创建 2 个线程

        pthread_create(&tid1, NULL, thread_fun_1, NULL);

        pthread_create(&tid2, NULL, thread_fun_2, NULL);

        // 等待线程结束,回收其资源

        pthread_join(tid1, NULL);

        pthread_join(tid2, NULL);

sem_destroy(&semA); //销毁信号量

        return 0;

}

编译及执行过程和运行结果截屏:

结果分析:

在主函数中,创建了两个线程,分别调用了thread_fun_1和thread_fun_2函数。这两个函数内部调用了printer函数。由于两个线程同时调用printer函数,但是通过信号量的控制,确保了只有一个线程可以打印字符,另一个线程在sem_wait(&semA)处等待。因此,打印的结果是"helloworld"。

2、通过多线程模拟多窗口售票,在主线程下创建4个子线程,模拟4个售票窗口,假设有20张票待售,运行该程序看会有什么样的结果,分析程序和执行结果。(1分)

<参考程序>

#include<pthread.h>

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<semaphore.h>

#include<stdint.h>

int ticket_sum=20;

void *sell_ticket(void *arg)

{

    int i;

    for(i=0;i<20;i++)

    {

        if(ticket_sum>0)

        {          

sleep(1);

            printf("sell the %dth\n",20-ticket_sum+1);

            ticket_sum--;

        }

    }

    return 0;

}

int main()

{

        int flag,i;

        pthread_t tids[4];

        for(i=0;i<4;i++)

        {

            flag=pthread_create(&tids[i],NULL,&sell_ticket,NULL);//创建线程

            if(flag)

            {

                printf("pthread create error ,flag=%d",flag);

                return flag;

            }

        }

        sleep(20);

        void *ans;

        for(i=0;i<4;i++)

        {

            flag=pthread_join(tids[i],&ans);//等待线程结束

            if(flag)

            {

                printf("tid=%lu,join erro flag=%d",tids[i],flag);

                return flag;

            }

            printf("ans=%ld\n",(intptr_t)ans);

            }

            return 0;

}给出编译及执行过程和运行结果:(部分截屏)

结果分析:

该程序创建了4个线程,每个线程都执行sell_ticket函数。在sell_ticket函数中,使用一个循环进行售票操作,每次售票后ticket_sum减1,直到ticket_sum为0结束循环。

在主函数中,创建4个线程,并等待线程完成。等待线程完成后,打印线程的返回值,此处返回值为0。

3、修改上题,用锁机制实现线程互斥进入临界区,解决售票窗口超卖问题。要求给出编译及运行过程和结果截图。 (2分)

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

#include<semaphore.h>

#include<stdint.h>

int ticket_sum = 20; // 总票数

pthread_mutex_t mutex; // 定义互斥锁

void *sell_ticket(void *arg)

{

    int ticket_count = 0; // 售出的票数

    while (1) {

        pthread_mutex_lock(&mutex); // 上锁

        if (ticket_sum > 0) {

            sleep(1); // 模拟售票耗时

            printf("sell the %d th\n", 20 - ticket_sum + 1);

            ticket_sum--;

            ticket_count++;

        } else {

            pthread_mutex_unlock(&mutex); // 解锁

            break;

        }

        pthread_mutex_unlock(&mutex); // 解锁

    }

    pthread_exit((void *)ticket_count); // 返回售出的票数

}

int main()

{  

    pthread_t tids[4]; // 线程数组

    int i;

    int ticket_count = 0; // 总共售出的票数

    pthread_mutex_init(&mutex, NULL); // 初始化互斥锁

    for (i = 0; i < 4; i++) {

        pthread_create(&tids[i], NULL, sell_ticket, NULL); // 创建线程

    }

    for (i = 0; i < 4; i++) {

        void *ret;

        pthread_join(tids[i], &ret); // 等待线程结束

        ticket_count += (intptr_t)ret; // 统计每个线程售出的票数

    }

    printf("总共售出了 %d 张票\n", ticket_count);

    pthread_mutex_destroy(&mutex); // 销毁互斥锁

    return 0;

}

给出编译及执行过程和运行结果:(部分截屏)

4、修改实验内容2,用信号量实现线程互斥进入临界区,解决售票窗口超卖问题。要求给出编译及执行过程和结果截屏。(2分)

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

#include<semaphore.h>

#include<stdint.h>

#define TOTAL_TICKETS 20

int tickets = TOTAL_TICKETS;

sem_t semaphore;

void *sellTicket(void *arg) {

    char *sellerName = (char *)arg;

    while (1) {

        sem_wait(&semaphore);  // 获得信号量,进入临界区

        if (tickets > 0) {

            tickets--;

            printf("sell the %dth\n", 20-tickets);

        } else {

            printf("没有票了\n");

            sem_post(&semaphore);  // 离开临界区

            break;

        }

        sem_post(&semaphore);  // 离开临界区

        usleep(100000);  // 模拟售票过程的耗时操作

    }

    return NULL;

}

int main() {

    pthread_t sellers[4];

    sem_init(&semaphore, 0, 1);

    char *sellerNames[4] = {"售票窗口1", "售票窗口2", "售票窗口3", "售票窗口4"};

    for (int i=0; i<4; i++) {

        pthread_create(&sellers[i], NULL, sellTicket, (void *)sellerNames[i]);

    }

    for (int i=0; i<4; i++) {

        pthread_join(sellers[i], NULL);

    }

    sem_destroy(&semaphore);

    return 0;

}

给出编译及执行过程和运行结果:(部分截屏)

5、利用线程和信号量机制实现司机售票员同步操作问题。(1分)

参考程序框架:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

#include <semaphore.h>

sem_t door, stop; // 设置关门和停车两个信号量

void *thread_driver(void *arg) // 司机线程

{

    while (1)

    {

        sem_wait(&door); // P(door),等待售票员的关门信号

        printf("司机: 启动汽车\n");

        printf("司机: 驾驶汽车\n");

        sleep(1);

        printf("司机: 到站停车\n");

        sem_post(&stop); // V(stop),发送停车信号

    }

}

void *thread_conductor(void *arg) // 售票员线程

{

    while (1)

    {

        printf("售票员: 关门\n");

        sem_post(&door); // V(door),发送关门信号

        printf("售票员: 卖票\n");

        sem_wait(&stop); // P(stop),等待司机的停车信号

        printf("售票员: 开门\n");

        printf("乘客上下车\n");

        sleep(1);

    }

}

int main()

{

    int sg1, sg2;

    pthread_t driver, conductor; // 定义两个变量存放线程标识符

    sg1 = sem_init(&door, 0, 0); // 初始化关门信号量door,初始值为0

    sg2 = sem_init(&stop, 0, 0); // 初始化停车信号量stop,初始值为0

    pthread_create(&driver, NULL, (void *)thread_driver, NULL);      // 创建司机线程

    pthread_create(&conductor, NULL, (void *)thread_conductor, NULL); // 创建售票员线程

    pthread_join(driver, NULL);     // 等待司机线程结束

    pthread_join(conductor, NULL);  // 等待售票员线程结束

    sem_destroy(&door);  // 销毁关门信号量

    sem_destroy(&stop);  // 销毁停车信号量

    return 0;

}

编译及执行过程和结果截屏:

6、利用线程和信号量实现生产者消费者问题(涉及线程同步和互斥问题)。(附加题)

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <pthread.h>

#include <semaphore.h>

#define BUFFER_SIZE 5

int buffer[BUFFER_SIZE];

sem_t mutex, empty, full;

int in = 0;

int out = 0;

void *producer(void *arg) {

    int item;

    while (1) {

        item = rand() % 100; // 生产一个随机数作为产品

        sem_wait(&empty); // 等待空缓冲区

        sem_wait(&mutex); // 互斥访问缓冲区

        printf("Produced item: %d\n", item);

        buffer[in] = item;

        in = (in + 1) % BUFFER_SIZE;

        sem_post(&mutex); // 释放互斥锁

        sem_post(&full); // 增加满缓冲区的信号量

        sleep(1); // 生产速度较慢,方便观察

    }

}

void *consumer(void *arg) {

    int item;

    while (1) {

        sem_wait(&full); // 等待满缓冲区

        sem_wait(&mutex); // 互斥访问缓冲区

        item = buffer[out];

        out = (out + 1) % BUFFER_SIZE;

        printf("Consumed item: %d\n", item);

        sem_post(&mutex); // 释放互斥锁

        sem_post(&empty); // 增加空缓冲区的信号量

        sleep(1); // 消费速度较慢,方便观察

    }

}

int main() {

    pthread_t producer_thread, consumer_thread;

    sem_init(&mutex, 0, 1); // 初始化互斥锁

    sem_init(&empty, 0, BUFFER_SIZE); // 初始化空缓冲区的信号量

    sem_init(&full, 0, 0); // 初始化满缓冲区的信号量

    pthread_create(&producer_thread, NULL, producer, NULL);

    pthread_create(&consumer_thread, NULL, consumer, NULL);

    pthread_join(producer_thread, NULL);

    pthread_join(consumer_thread, NULL);

    sem_destroy(&mutex); // 销毁互斥锁

    sem_destroy(&empty); // 销毁空缓冲区的信号量

    sem_destroy(&full); // 销毁满缓冲区的信号量

    return 0;

}

编译及执行过程和结果截屏:

三、实验总结和体会

通过这次实验,我对操作系统中多线程编程有了更深入的了解和实践经验。在实验中,我学习了使用多线程编程来实现并发任务的处理,以及线程间的同步和互斥机制。

首先,我学会了使用线程创建函数来创建和启动新线程。我了解到线程是程序中的并发执行流,可以并行处理任务,提高程序的执行效率。在实验中,我通过使用pthread库中的函数,如pthread_create()来创建线程,并指定线程函数进行任务处理。

其次,我学会了使用互斥锁来保护共享资源的访问。在多线程编程中,当多个线程需要同时访问共享资源时,可能会导致竞态条件和数据不一致的问题。为了避免这些问题,我使用了互斥锁来实现线程间的互斥访问。通过使用pthread库中的函数,如pthread_mutex_init()和pthread_mutex_lock()等,我可以对关键代码段进行上锁,使得同一时间只有一个线程可以访问共享资源。

此外,我还学习了使用条件变量来实现线程间的通信和同步。条件变量可以用于线程间的等待和唤醒操作,使得线程可以在特定条件满足时才进行执行。通过使用pthread库中的函数,如pthread_cond_init()和pthread_cond_wait()等,我可以实现线程的等待和唤醒操作,以实现线程间的同步和协作。

通过这次实验,我对操作系统中多线程编程的原理和实践有了更深入的理解。我认识到多线程编程可以提高程序的并发性和效率,但也需要考虑线程间的同步和互斥,以及资源管理和性能优化等方面。我意识到多线程编程的复杂性和挑战,但也体会到了并发编程的用处

相关文章:

  • [蓝桥杯 2025 省 Python B] 最多次数
  • HashedWheelTimer源码分析
  • Scrapy框架爬虫官网的学习
  • OpenCV 图形API(55)颜色空间转换-----将图像从 RGB 色彩空间转换为 I420 格式函数RGB2I420()
  • 第九章:Agent Protocol Implementation
  • 香港云服务器内存使用率过高如何解决此问题
  • PH热榜 | 2025-04-23
  • 【金仓数据库征文】从 HTAP 到 AI 加速,KingbaseES 的未来之路
  • 《AI大模型应知应会100篇》第35篇:Prompt链式调用:解决复杂问题的策略
  • day4 pandas学习
  • godot源码编译
  • Oracle EBS R12.2 汉化
  • Java从入门到“放弃”(精通)之旅——String类⑩
  • C#学习1_认识项目/程序结构
  • SAP-SD创建SO时报错‘送达方***未对销售范围 **** ** **定义’
  • App自动化测试多设备并行执行方案
  • jumpserver应用
  • MinIO 教程:从入门到Spring Boot集成
  • 【maven-7.1】POM文件中的属性管理:提升构建灵活性与可维护性
  • VS 解决QT项目中文显示乱码问题
  • A股三大股指涨跌互现,工农中三大行股价创新高
  • 解密帛书两千年文化传承,《帛书传奇》央视今晚开播
  • 美股反弹,纳斯达克中国金龙指数大涨3.69%
  • 童书湃|世界读书日:在书里去辽阔的自然里撒个欢
  • 十二届上海市委第六轮巡视启动,对18家市管单位开展常规巡视
  • 诸葛燕喃出任中央文化和旅游管理干部学院党委书记