作业一:创建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;
}
作业三:互斥锁,无名信号量,条件变量再联系一遍。
思维导图:





1449

被折叠的 条评论
为什么被折叠?



