作业一:创建3个线程,一个子线程拷贝文件的前一半,一个子线程拷贝后一半文件,主线程回收子线程资源。
知识点:线程相较于进程,能够很方便的共享数据,因为多个线程对于全局变量修改读取同一个地址。
因为线程的执行由cpu决定,并发时具有不可预测的特点,因此需要特别注意多个线程访问同一个地址的或者输出的顺序问题,一般采用以下方法对地址进行保护。
- 进程互斥锁:适合在多线程间保护共享资源,防止竞争条件。
- 无名信号量:适合在生产者-消费者模型中控制线程对资源的并发访问。
- 条件变量:适合用于实现线程的同步,等待特定的条件完成。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#define BUF_SIZE 1000
typedef struct{
FILE *source_file;
FILE *dest_file;
long file_start;
long file_end;
}ThreadData;
void *copy_half(void *arg){
//获取创建线程传递的参数
ThreadData *data=(ThreadData*)arg;
//将文件指针设置到开始位置
fseek(data->source_file,data->file_start,SEEK_SET);
fseek(data->dest_file,data->file_start,SEEK_SET);
//每次读取,写值和剩余值
size_t f_read,f_write,remaining=data->file_end-data->file_start;
//缓冲区
char buf[BUF_SIZE];
while(1){
f_read=remaining>BUF_SIZE?BUF_SIZE:remaining;
f_write=fread(buf,1,f_read,data->source_file);
//如果读取值为0退出,注意不可以用剩余值<=0,因为可能因为读取值为0,剩余值-=写入值不<=0
if(f_write<=0)break;
f_write=fwrite(buf,1,f_write,data->dest_file);
remaining-=f_write;
}
return NULL;
}
int main(int argc, const char *argv[])
{
//打开文件
FILE *src=fopen("1.txt","rb");
FILE *src2=fopen("1.txt","rb");
FILE *des=fopen("2.txt","wb");
if(!src|!des){
printf("文件打开失败");
return -1;
}
//设置指针到文件末尾计算文件大小,和文件一半。
fseek(src,0,SEEK_END);
long file_size=ftell(src);
long half_size=file_size/2;
//赋值参数
ThreadData data1={src,des,0,half_size};
ThreadData data2={src2,des,half_size,file_size};
pthread_t thread1,thread2;//一般这些_t都是type的意思统一用于跨平台。
//创建线程,参数一为储存线程Id,2为线程属性结构体,填写NULL为默认。3.要执行的函数。4.参数
pthread_create(&thread1,NULL,copy_half,&data1);
pthread_create(&thread2,NULL,copy_half,&data2);
//等待回收线程,参数1为要回收的线程Id,2为储存的线程结束返回值
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
//关闭打开的文件。
fclose(src);
fclose(src2);
fclose(des);
return 0;
}
作业二:使用无名信号量实现循环输出春、夏、秋、冬。
sem_init通知操作系统创建初始化信号量,这个信号量会在内部维护一个计数器和阻塞队列。在通过sem_post和sem_wait来修改信号量,进而实现间接控制阻塞队列,实现线程的执行与阻塞。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
//定义sem变量
sem_t sem_spring,sem_summer,sem_autumn,sem_winter;
void *print_spring(void *args){
while(1){
//减信号量
sem_wait(&sem_spring);
printf("春\n");
//加信号量
sem_post(&sem_summer);
}
}
void *print_summer(void *args){
while(1){
sem_wait(&sem_summer);
printf("夏\n");
sem_post(&sem_autumn);
}
}
void *print_autumn(void *args){
while(1){
sem_wait(&sem_autumn);
printf("秋\n");
sem_post(&sem_winter);
}
}
void *print_winter(void *args){
while(1){
sem_wait(&sem_winter);
printf("冬\n");
sem_post(&sem_spring);
}
}
int main(int argc, const char *argv[])
{
//初始化信号量,参数为信号量地址,是否进程间通信,初始值。
sem_init(&sem_spring,0,1);
sem_init(&sem_summer,0,0);
sem_init(&sem_autumn,0,0);
sem_init(&sem_winter,0,0);
pthread_t p1,p2,p3,p4;
pthread_create(&p1,NULL,print_spring,NULL);
pthread_create(&p2,NULL,print_summer,NULL);
pthread_create(&p3,NULL,print_autumn,NULL);
pthread_create(&p4,NULL,print_winter,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_join(p3,NULL);
pthread_join(p4,NULL);
//销毁信号量
sem_destroy(&sem_spring);
sem_destroy(&sem_summer);
sem_destroy(&sem_autumn);
sem_destroy(&sem_winter);
return 0;
}