8.27练习

news2024/9/24 15:25:19

在这里插入图片描述

一.创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件

创建两个子线程分别负责拷贝文件的前半段和后半段,从而提高文件拷贝的效率。父线程负责创建和管理子线程,并等待它们完成任务。

#include <myhead.h>

typedef struct {
    const char *p1; // 源文件地址
    const char *p2; // 目标文件地址
    int start;      // 光标起始地址
    int len;        // 拷贝长度
} File;

// 获取源文件的长度,并创建目标文件
int get_len(const char *p1, const char *p2) {
    int fd1, fd2;
    fd1 = open(p1, O_RDONLY);
    if (fd1 == -1) {
        perror("open 1");
        return -1;
    }
    fd2 = open(p2, O_CREAT | O_RDWR | O_TRUNC, 0664); // 创建清空打开文件
    if (fd2 == -1) {
        perror("open 2");
        return -1;
    }
    int len = lseek(fd1, 0, SEEK_END);
    
    close(fd1);
    close(fd2);
    return len;
}

// 拷贝文件的指定部分
void *copy_file(void *FFF) {
    File *bz = (File*)FFF; // 强制类型转换

    int fd1, fd2;
    if ((fd1 = open(bz->p1, O_RDONLY)) == -1) {
        perror("open 1");
        return NULL;
    }
    if ((fd2 = open(bz->p2, O_WRONLY)) == -1) {
        perror("open 2");
        return NULL;
    }
    lseek(fd1, bz->start, SEEK_SET); // fd1和fd2的光标同步移动
    lseek(fd2, bz->start, SEEK_SET);
    int sum = 0;
    char buff[1024];
    while (1) {
        memset(buff, 0, sizeof(buff)); // 清空buff缓冲区
        int res = read(fd1, buff, sizeof(buff));
        sum += res; // 记录字符长度的总和
        if (sum >= (bz->len) || res == 0) { // 当总和大于长度时,或者读取到文件末尾
            write(fd2, buff, res - (sum - bz->len)); // 写入剩余的部分
            break;
        }
        write(fd2, buff, res); // sum<len时,每次写入res个字符
    }

    close(fd1);
    close(fd2);
    return NULL;
}

int main(int argc, const char *argv[]) {
    pthread_t tid1, tid2;

    if (argc != 3) {
        printf("外部传参错误!\n");
        return -1;
    }
    int len = get_len(argv[1], argv[2]); // 获取文件长度
    
    File FFF[2] = {{argv[1], argv[2], 0, len / 2}, {argv[1], argv[2], len / 2, len - (len / 2)}};

    if (pthread_create(&tid1, NULL, copy_file, &FFF[0]) != 0) { // FFF[0]作为copy_file线程体函数的参数
        perror("tid1_create");
        return -1; 
    }
    if (pthread_create(&tid2, NULL, copy_file, &FFF[1]) != 0) { // FFF[0]作为copy_file线程体函数的参数
        perror("tid2_create");
        return -1; 
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}
代码解释
  1. 定义文件结构体

    • File 结构体包含源文件地址、目标文件地址、光标起始地址和拷贝长度。
  2. 获取文件长度并创建目标文件

    • get_len 函数负责打开源文件 p1 并计算其长度,同时创建目标文件 p2
    • 使用 lseek 函数将文件指针移动到文件末尾,从而获取文件长度。
  3. 拷贝文件的指定部分

    • copy_file 函数负责从源文件 p1 中读取数据并写入目标文件 p2
    • 函数接受一个 File 结构体指针作为参数,包含拷贝的起始位置和长度。
    • 使用 lseek 函数将文件指针移动到指定的起始位置,然后循环读取和写入数据,直到拷贝完成指定长度的数据。
  4. 主函数

    • 检查命令行参数的数量,确保传入了源文件和目标文件的路径。
    • 调用 get_len 函数获取源文件的长度,并创建目标文件。
    • 创建两个子线程,分别负责拷贝文件的前一半和后一半。
    • 父线程等待两个子线程完成。
运行结果

运行该程序后,目标文件 destination.txt 将包含源文件 source.txt 的完整内容。两个子线程分别负责拷贝文件的前半段和后半段,父线程负责回收子线程的资源。

总结

这段代码学习了如何使用多线程来实现文件的分段拷贝。多线程是一种非常有用的同步机制,可以确保多个线程按预期的顺序执行。在实际应用中,这种方法可以用于提高文件拷贝的效率,特别是对于大文件的拷贝。

二. 使用信号量和多线程实现季节循环打印

下面将介绍如何使用信号量和多线程来实现一个简单的季节循环打印程序。该程序将四个季节(春、夏、秋、冬)按顺序打印,并且每个季节的名称打印后会等待一秒钟。

代码实现
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

sem_t sem1, sem2, sem3, sem4;

// fun1 线程:打印 "春"
void *fun1(void *as) {
    while (1) {
        sem_wait(&sem4); // 等待 sem4 信号量
        printf("春\t");
        fflush(stdout); // 刷新输出缓冲区
        sleep(1);
        sem_post(&sem3); // 释放 sem3 信号量
    }
}

// fun2 线程:打印 "夏"
void *fun2(void *as) {
    while (1) {
        sem_wait(&sem3); // 等待 sem3 信号量
        printf("夏\t");
        fflush(stdout); // 刷新输出缓冲区
        sleep(1);
        sem_post(&sem2); // 释放 sem2 信号量
    }
}

// fun3 线程:打印 "秋"
void *fun3(void *as) {
    while (1) {
        sem_wait(&sem2); // 等待 sem2 信号量
        printf("秋\t");
        fflush(stdout); // 刷新输出缓冲区
        sleep(1);
        sem_post(&sem1); // 释放 sem1 信号量
    }
}

// fun4 线程:打印 "冬"
void *fun4(void *as) {
    while (1) {
        sem_wait(&sem1); // 等待 sem1 信号量
        printf("冬\t");
        fflush(stdout); // 刷新输出缓冲区
        sleep(1);
        sem_post(&sem4); // 释放 sem4 信号量
        putchar(10); // 换行
    }
}

int main(int argc, const char *argv[]) {
    pthread_t tid1, tid2, tid3, tid4;

    // 初始化无名信号量
    sem_init(&sem1, 0, 0);
    sem_init(&sem2, 0, 0);
    sem_init(&sem3, 0, 0);
    sem_init(&sem4, 0, 1); // 初始为1,表示第一个打印 "春"

    // 创建线程
    if (pthread_create(&tid1, NULL, fun1, NULL) != 0) { // 1线程
        perror("tid1");
        return -1;
    }

    if (pthread_create(&tid2, NULL, fun2, NULL) != 0) { // 2线程
        perror("tid2");
        return -1;
    }

    if (pthread_create(&tid3, NULL, fun3, NULL) != 0) { // 3线程
        perror("tid3");
        return -1;
    }

    if (pthread_create(&tid4, NULL, fun4, NULL) != 0) { // 4线程
        perror("tid4");
        return -1;
    }

    // 等待线程完成
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);

    // 销毁信号量
    sem_destroy(&sem1);
    sem_destroy(&sem2);
    sem_destroy(&sem3);
    sem_destroy(&sem4);

    return 0;
}
代码解释

信号量初始化:

  • sem1, sem2, sem3, sem4 是四个无名信号量,用于控制线程的执行顺序。

  • sem4 初始值为 1,表示第一个打印 “春” 的线程可以立即执行。

  • 其他信号量初始值为 0,表示这些线程需要等待信号量被释放后才能执行。

线程函数:

  • fun1 线程:等待 sem4 信号量,打印 “春”,然后释放 sem3 信号量。

  • fun2 线程:等待 sem3 信号量,打印 “夏”,然后释放 sem2 信号量。

  • fun3 线程:等待 sem2 信号量,打印 “秋”,然后释放 sem1 信号量。

  • fun4 线程:等待 sem1 信号量,打印 “冬”,然后释放 sem4 信号量,并换行。

主函数:

  • 初始化信号量。

  • 创建四个线程,分别执行 fun1, fun2, fun3, fun4 函数。

  • 等待所有线程完成。

  • 销毁信号量。

运行结果

运行该程序后,终端将按顺序循环打印四个季节的名称,每个季节的名称打印后会等待一秒钟。输出示例如下:

春    夏    秋    冬
春    夏    秋    冬
春    夏    秋    冬
...
总结

通过这段代码,我学习了如何使用信号量来控制多线程程序的执行顺序。信号量是一种非常有用的同步机制,可以确保多个线程按预期的顺序执行。在实际应用中,信号量可以用于解决各种同步问题,例如生产者-消费者问题、读者-写者问题等。希望这篇博客对你理解信号量和多线程编程有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2081074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第377题组合总和IV

题目&#xff1a; 题解&#xff1a; func combinationSum4(nums []int, target int) int {dp : make([]int, target1)dp[0] 1for i : 1; i < target; i {for _, num : range nums {if num < i {dp[i] dp[i-num]}}}return dp[target] }

如何使用 Pytest 进行测试

Pytest 是一个强大的Python测试框架&#xff0c;支持简单单元测试和复杂的功能测试。它具有灵活的断言表达式、支持参数化测试、强大的插件生态系统等特点。 二、环境搭建 1、安装 Pytest&#xff1a; pip install pytest安装完成后&#xff0c;可以通过命令行检查是否安装成…

锡废水回收实验

电镀厂的锡废水回收过程中&#xff0c;需要进行一系列实验以确保回收效率和环保标准。这些实验主要包括以下几个方面&#xff1a; 一、废水成分分析实验 目的&#xff1a;了解废水中锡及其他重金属、有机物、悬浮物等的含量和性质&#xff0c;为后续处理工艺提供依据。方法&am…

‌分布式计算技术与复杂算法优化:‌现代数据处理的基石

在当今信息时代&#xff0c;‌数据已成为企业和社会发展的核心资源。‌随着数据量的爆炸性增长&#xff0c;‌传统的单机计算模式已难以满足高效数据处理的需求。‌分布式计算技术应运而生&#xff0c;‌它通过将大规模计算任务分解成多个子任务&#xff0c;‌在多个计算节点上…

Docker Compose 部署 Kafka的KRaft模式 不用依赖 Zookeeper

前言 从 Kafka 2.8 开始&#xff0c;KRaft 模式允许 Kafka 在没有 Zookeeper 的情况下运行。本文将部署单机模式 1.新建 docker-compose.yml vim docker-compose.yml services:kafka:image: bitnami/kafka:3.6container_name: kafkaports:- "19092:9092"environm…

2024华为数通HCIP-datacom最新题库(H12-831变题更新⑩)

请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了&#xff0c;如果你准备去考试&#xff0c;还是用的之前的题库&#xff0c;切记暂缓。 1、…

SpringBoot整合SpringSecurity + JWT

SpringBoot整合SpringSecurity JWT 前置知识&#xff1a;Cookie&#xff0c;Session&#xff0c;Token Cookie&#xff0c;Session介绍 Cookie 、 Session 和 Token 是用于在 Web 应用程序中管理用户状态和身份验证的技术。因为在 Web 应用中&#xff0c; HTTP的通信是无状…

基于单片机的智能防盗窗户的设计

本设计是一种基于单片机的智能防盗窗户&#xff0c;采用STC89C52单片机作为核心控制器&#xff0c;配合DHT11温湿度传感器和MQ-2烟雾传感器等传感器和模块&#xff0c;用于收集室内环境数据&#xff0c;并通过单片机进行数据处理和控制。实现窗户状态的智能监测和防盗报警&…

CSEC:香港城市大学提出SOTA曝光矫正算法 | CVPR 2024

在光照条件不佳下捕获的图像可能同时包含过曝和欠曝。目前的方法主要集中在调整图像亮度上&#xff0c;这可能会加剧欠曝区域的色调失真&#xff0c;并且无法恢复过曝区域的准确颜色。论文提出通过学习估计和校正这种色调偏移&#xff0c;来增强既有过曝又有欠曝的图像。先通过…

HR人力资源OKR示例

O&#xff1a;提高内部员工敬业度和工作满意度 KR1: 开展9月每周五全体员工的会议与励志演讲主题 KR2: 采访36名员工&#xff0c;了解他们改善工作文化的需求 KR3: 在所有16个部门中使用OKR和Tita软件实施 KR4: 达到至少每周员工的满意度&#xff1a;4.7分 O&#xff1a;招聘工…

如何确定电脑与PLC有没有链接上

直接Ping一下 第一步&#xff1a;winR调出运行框 第二步&#xff1a;输入CMD-点确认 弹出运行框在里面输入Ping 192.168.10.1 失败状态 成功状态

关于Flink内存分配核心知识点

这个问题同样也是之前辅导过的同学的面试问题&#xff0c;这个问题非常接地气且考察面试者的实践经验。事实上&#xff0c;这也是我们大数据提高班的Flink专项提高部分内容。 下面我列举的这些就是核心&#xff0c;能答出这些重点即可。 内存模型在Flink1.9和Flink1.11版本做了…

EmguCV学习笔记 VB.Net 7.1 角点检测

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

【React】跨域问题详解及解决方案

文章目录 一、什么是跨域问题&#xff1f;1. 同源策略的定义2. CORS 机制 二、在 React 项目中遇到的跨域问题常见的跨域错误信息 三、解决 React 中跨域问题的方法1. 在后端服务器上配置 CORS2. 在 React 项目中使用代理 (Proxy)2.1 使用 http-proxy-middleware 实现代理1. 安…

SAP主数据删除

项目场景&#xff1a; SAP项目上线初期&#xff0c;主数据批到相关的操作 操作描述 需要对供应商&#xff0c;客户&#xff0c;科目进行维护操作&#xff0c;比如这些数据创建错误&#xff0c;不想留一些垃圾数据在系统里面 解决方案&#xff1a; 事务代码&#xff1a;OBR2…

低侧与高侧电流检测对比

1 简介 在处理低至中等电流水平时&#xff0c;电阻电流检测广泛用于印刷电路板组件。使用这种技术&#xff0c;将一个已知的电阻 R分流器与负载串联&#xff0c;并测量电阻两端的电压以确定负载电流。如下图所示。 电流检测电阻器&#xff0c;也称为分流电阻器或简称为分流器&a…

Python TensorFlow实战篇

概述 本篇博客将详细介绍如何使用Python和TensorFlow解决实际问题&#xff0c;包括图像分类、序列预测以及模型部署等内容。我们将从以下几个方面进行深入探讨&#xff1a; 图像分类实战&#xff1a;使用卷积神经网络&#xff08;CNN&#xff09;进行图像分类。序列预测实战&…

模型 KT决策法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。系统分析&#xff0c;明智选择。 1 KT决策法的应用 1.1 餐饮连锁店菜单更新 一家餐饮连锁店计划更新菜单&#xff0c;以吸引更多顾客并提高销售额。使用 KT 决策法&#xff08;Kepner-Tregoe&#x…

哪些领域最适合采用音视频私有化解决方案?

随着数字化时代的到来&#xff0c;音视频通信已成为各行各业不可或缺的一部分&#xff0c;从企业内部沟通到在线教育、远程医疗、金融交易等&#xff0c;无一不依赖于稳定、高效且安全的音视频技术。然而&#xff0c;不同的行业对音视频通信的需求各不相同&#xff0c;尤其在数…

【Denuvo加密】黑神话悟空为什么没有破解版?Denuvo加密技术的详细解析与代码示例

文章目录 1. 引言2. 加密技术概述3. D加密技术的工作原理4. D加密技术的实现5. D加密技术的实际应用6.实现原理7. 本篇小结 更多相关内容可查看 1. 引言 随着游戏产业的蓬勃发展&#xff0c;游戏数据的保护成为了重要议题。《黑神话&#xff1a;悟空》作为一款备受期待的游戏&…