3day6(线程间的通信)

作业一:创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件,主线程回收子线程资源。

(外部传参版)

#include <myhead.h>

//作业一:创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件,主线程回收子线程资源。
int get_len(const char *p1, const char *p2)
{
    int fd1 = open(p1, O_RDONLY); // 打开源文件
    if (fd1 == -1)
    {
        perror("open1");
        return -1;
    }
    int fd2 = open(p2, O_WRONLY | O_CREAT | O_TRUNC, 0664); // 打开创建目标文件
    if (fd2 == -1)
    {
        perror("open2");
        close(fd1);
        return -1;
    }
    int len = lseek(fd1, 0, SEEK_END); // 计算源文件长度
    close(fd1);
    close(fd2);
    return len;
}

void* copy_file(void *arg)
{
    char **args = (char**)arg;
    const char *src = args[0];
    const char *dest = args[1];
    int start = atoi(args[2]);
    int len = atoi(args[3]);

    int fd1, fd2;
    fd1 = open(src, O_RDONLY); // 只读方式打开源文件
    if (fd1 == -1)
    {
        perror("open1");
        pthread_exit(NULL);
    }
    fd2 = open(dest, O_WRONLY); // 只写方式打开目标文件
    if (fd2 == -1)
    {
        perror("open2");
        close(fd1);
        pthread_exit(NULL);
    }

    lseek(fd1, start, SEEK_SET);
    lseek(fd2, start, SEEK_SET);
    char buff[1024];
    int sum = 0;
    while (1)
    {
        int res = read(fd1, buff, sizeof(buff));
        sum += res;
        if (sum >= len || res == 0)
        {
            write(fd2, buff, res - (sum - len));
            break;
        }
        write(fd2, buff, res);
    }
    close(fd1);
    close(fd2);
    pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
    if (argc != 3)
    {
        printf("外部传参错误\n");
        return -1;
    }
    int len = get_len(argv[1], argv[2]); // 获取源文件的长度,打开创建目标文件
    if (len == -1)
    {
        return -1;
    }

    pthread_t tid1, tid2;
    char *args1[4] = {(char*)argv[1], (char*)argv[2], "0", malloc(20)};
    char *args2[4] = {(char*)argv[1], (char*)argv[2], malloc(20), malloc(20)};
    
    sprintf(args1[3], "%d", len/2);
    sprintf(args2[2], "%d", len/2);
    sprintf(args2[3], "%d", len - len/2);

    // 创建第一个线程拷贝前一半
    if (pthread_create(&tid1, NULL, copy_file, args1) != 0)
    {
        perror("pthread_create");
        return -1;
    }

    // 创建第二个线程拷贝后一半
    if (pthread_create(&tid2, NULL, copy_file, args2) != 0)
    {
        perror("pthread_create");
        return -1;
    }

    // 等待两个线程结束
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    // 释放动态分配的内存
    free(args1[3]);
    free(args2[2]);
    free(args2[3]);

    printf("文件拷贝完成\n");
    return 0;
}

(外部传参,注释多一点,后续可更改)

#include <myhead.h>
//作业一:创建3个线程,一个子线程拷贝文件的前一半,
//一个子线程拷贝后一半文件,主线程回收子线程资源。
//
#define AZE 10//读取时中间变量数组的大小(缓冲区的大小)

//全局变量
int fd_src;//源文件的目标描述符
int fd_dest;//目标文件的文件描述符
off_t file_size;//源文件的大小

void *copy_my_first(void * arg)//读取上半文件并拷贝
{
	char buff[AZE];//中间变量数组
	ssize_t bytes_read;//接受读取函数的返回值
	ssize_t bytes_write;//接收写入函数的返回值
	off_t current_pos =0;//光标所在位置(已经读取到的位置,防止超过一半)

	while (current_pos < file_size/2)//循环读取并拷贝到一半
	{
		bytes_read = read(fd_src,buff,(file_size/2-current_pos < AZE)?
				(file_size/2-current_pos):AZE);//此处用三目运算符代替下面的判断运算,
		//结果一样,效果简化了代码,提高了可阅读性
#if 0
		//等价于
		int len = file_size/2-current_pos;//计算是
				if(len<AZE)//判断是否到达中点
		{
			len = AZE;//没有到达中点则继续之前的大小读取
		}
		else//到达中点则按照剩余多少就取多大
		{
			len = file_size/2-current_pos;
		}
		bytes_read = read (fd_src,buff,len);

#endif
		if(bytes_read <= 0) break;//简化了读取错误时和读到文件末尾时的操作
		//无法区分是否读取错误,但缩减了代码量
		//等价于(不完全等价)
#if 0
		if(bytes_read = -1)
		{
			perror("bytes_read");
			break;
		}
		if(bytes_read = 0)
		{
			printf("文件前半部分读取完毕\n");
		}
#endif
	  bytes_write = write(fd_dest, buff, bytes_read);
	  //文件写入(拷贝)
        if (bytes_write != bytes_read) //错误则退出循环
		{
            perror("Write error");
            break;
        }

        current_pos += bytes_read;//光标位置记录(依靠读取返回值的累加)
    }

    pthread_exit(NULL);//结束线程
}

// 拷贝文件后半部分的线程函数
void* copy_second_half(void* arg) 
{
   	char buff[AZE];//中间变量数组
	ssize_t bytes_read;//接受读取函数的返回值
	ssize_t bytes_write;//接收写入函数的返回值
	off_t current_pos =0;//光标所在位置(已经读取到的位置,防止超过一半)

    // 将文件指针移动到文件中间
    lseek(fd_src, file_size / 2, SEEK_SET);//光标置位到中间

    while (current_pos < file_size) 
	{
        // 读取数据,确保不会读取超过文件末尾
        bytes_read = read(fd_src, buff,
                          (file_size - current_pos < AZE) ?
                          (file_size - current_pos) : AZE);

        if (bytes_read <= 0) break;  // 读取错误或到达文件末尾

        // 写入数据到目标文件
        bytes_write = write(fd_dest, buff, bytes_read);
        if (bytes_write != bytes_read) 
		{
            perror("Write error");
            break;
        }

        current_pos += bytes_read;
    }

    pthread_exit(NULL);
}
//主函数
int main(int argc, const char *argv[])
{
	  // 检查命令行参数
    if (argc != 3) 
	{
        fprintf(stderr, "Usage: %s <source_file> <destination_file>\n", argv[0]);
		//stderr 标准错误流.
		//%s 是一个格式说明符,它会被后面的参数替换。
		//<source_file> 和 <destination_file> 是提示用户需要提供的参数。
		//这是程序的名称。在命令行中,argv[0] 通常包含了执行的程序名。
		//使用 %s 和 argv[0] 可以确保无论程序如何重命名,使用说明中都会显示正确的程序名。
        exit(1);//错误则结束程序运行
    }

    // 打开源文件
    fd_src = open(argv[1], O_RDONLY);//只读方式打开源文件
    if (fd_src == -1) 
	{
        perror("读取出错1");
        exit(1);//错误则结束程序运行

    }

    // 打开或创建(文件不存在则创建文件)目标文件
    fd_dest = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0664);//
    if (fd_dest == -1) 
	{
        perror("Error opening destination file");
        close(fd_src);
        exit(1);//错误则结束程序运行

    }

    // 获取源文件的大小
    struct stat st;
    if (fstat(fd_src, &st) != 0) 
	{
        perror("Error getting file size");
        close(fd_src);
        close(fd_dest);
        exit(1);//错误则结束程序运行

    }
    file_size = st.st_size;

    pthread_t thread1, thread2;//定义线程

    // 创建第一个线程拷贝前半部分
    if (pthread_create(&thread1, NULL, copy_my_first, NULL) != 0) 
	{
        perror("Error creating thread 1");
        close(fd_src);
        close(fd_dest);
        exit(1);
    }

    // 创建第二个线程拷贝后半部分
    if (pthread_create(&thread2, NULL, copy_second_half, NULL) != 0) 
	{
        perror("Error creating thread 2");
        close(fd_src);
        close(fd_dest);
        exit(1);
    }

    // 等待两个线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 关闭文件
    close(fd_src);
    close(fd_dest);

    printf("File copy completed.\n");

	return 0;
}

作业二:使用无名信号量实现循环输出 春、夏、秋、冬。

(稍改版)

include <myhead.h>

sem_t sem1,sem2,sem3,sem4;
void *fun1(void *ggg)
{
	while(1)
	{
		sem_wait(&sem4);
		printf("春天\t");
		fflush(stdout);
		sem_post(&sem3);

	}
	pthread_exit(NULL);
}
void *fun2(void *ggg)
{
	while(1)
	{
		sem_wait(&sem3);
		printf("夏天\t");
		fflush(stdout);
		sem_post(&sem2);
	}
	pthread_exit(NULL);
}
void *fun3(void *ggg)
{
	while(1)
	{
		sem_wait(&sem2);
		printf("秋天\t");
		fflush(stdout);
		sem_post(&sem1);
	}
	pthread_exit(NULL);
}
void *fun4(void *ggg)
{
	while(1)
	{
		sem_wait(&sem1);
		printf("冬天\t");
		fflush(stdout);
		sem_post(&sem4);
	}
	pthread_exit(NULL);
}

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);
	if(pthread_create(&tid1,NULL,fun1,NULL)!=0)
	{
		perror("ptcreat1");
		return -1;
	}
	if(pthread_create(&tid2,NULL,fun2,NULL)!=0)
	{
		perror("ptcreat2");
		return -1;
	}
	if(pthread_create(&tid3,NULL,fun3,NULL)!=0)
	{
		perror("ptcreat3");
		return -1;
	}
	if(pthread_create(&tid4,NULL,fun4,NULL)!=0)
	{
		perror("ptcreat4");
		return -1;
	}


	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);
	pthread_join(tid4,NULL);
	return 0;
}

作业三:互斥锁,无名信号量,条件变量再联系一遍。

思维导图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值