多线程
- 概念
- 多线程:线程的更小粒度的单元
- 线程是任务调度的最小单位,进程是资源分配的最小单位
- 线程几乎不占用进程资源,只是占用了很小的有关线程体的相关资源8k
- 多个线程共享进程的资源,所以,线程没有进程安全
- 在一个进程中,至少由一个线程(主线程)
- 线程所在的进程结束后,该进程中的所有线程全部结束
- 线程所在的进程结束后,该进程中的所有线程全部结束
- 进程中的多个线程的调度方式:时间片轮询,上下文切换,没有先后顺序
线程的创建(pthread_creat
)
-
功能
- 创建一个线程
-
原型
-
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
-
-
函数参数
pthread_t *thread
:线程号(通过地址返回)const pthread_attr_t *attr
:线程的属性,一般写NULLvoid*(*start_routine) (void *)
:线程体函数,是一个函数指针,参数和返回值都为void*类型void *arg
:万能指针,是线程体函数的参数
-
返回值
- 成功:返回0
- 失败:返回错误码,并且线程号不存在
线程号的获取(pthread_self
)
-
功能
- 获取当前线程的线程号
-
原型
-
#include <pthread.h> pthread_t pthread_self(void);
-
-
参数:无
-
返回值
- 总是成功,返回当前线程的线程号
线程退出函数(pthread_exit
)
-
功能
- 退出线程
-
原型
-
#include <pthread.h> void pthread_exit(void *retval);
-
-
参数
void *retval
:退出线程时的状态值,一般为NULL
-
返回值:无
线程回收函数(pthread_join
)
-
功能
- 阻塞函数,阻塞等待的指定线程退出,并回收资源
-
原型
-
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
-
-
参数
pthread_t thread
:指定要回收的线程,填对应的tid号void **retval
:一般为NULL
-
返回值
- 成功:返回0
- 失败:返回
error_number
向线程发信号(pthread_cancel
)
-
功能
- 向指定的线程发送取消信号,让该线程结束
-
原型
-
#include <pthread.h> int pthread_cancel(pthread_t thread);
-
-
参数
pthread_t thread
:指定要请求的线程tid号
-
返回值
- 成功:返回0
- 失败:返回error_number
设置线程取消状态(int pthread_setcancelstate
)
-
功能
- 设置线程取消的状态
-
原型
-
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate);
-
-
参数
int state
:要更改的线程状态PTHREAD_CANCEL_ENABLE
:可以接受取消请求(默认)PTHREAD_CANCEL_DISABLE
:不接受取消请求
int *oldstate
:旧的状态,一般写NULL
-
返回值
- 成功:返回0
- 失败:返回非0的错误码
分离线程(pthread_detach
)
-
功能
- 将指定的某个线程设置成分离态,然后设置成分离态的线程,当其结束时,系统会自动回收其资源,无需使用
pthread_join
来回收
- 将指定的某个线程设置成分离态,然后设置成分离态的线程,当其结束时,系统会自动回收其资源,无需使用
-
原型
-
#include <pthread.h> int pthread_detach(pthread_t thread);
-
-
参数
pthread_t thread
:指定要分离的线程(tid
)
-
返回值
- 成功:返回0
- 失败:返回错误码
利用多线程实现文件的拷贝
//两个线程实现拷贝
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
typedef struct file
{
char *str1;
char *str2;
} File_name_t;
void* task1(void *arg)
{
//创建两个文件描述符
int fd_r, fd_w;
//只读打开文件
fd_r = open((*(File_name_t *)arg).str1, O_RDONLY);
if(fd_r < 0)
{
perror("open error");
pthread_exit(NULL);
}
//只写打开文件
fd_w = open((*(File_name_t *)arg).str2, O_WRONLY);
if(fd_w < 0)
{
perror("open error");
close(fd_r);
pthread_exit(NULL);
}
//计算文件大小
off_t file_size = lseek(fd_r, 0, SEEK_END);
//将读写光标移动到文件开头
lseek(fd_r, 0, SEEK_SET);
lseek(fd_w, 0, SEEK_SET);
//以每次一个字节进行数据拷贝
char c = 0;
for(int i = 0; i < file_size/2; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c, 1);
}
//关闭文件,退出线程
puts("前半部分拷贝完毕");
close(fd_r);
close(fd_w);
pthread_exit(NULL);
}
void* task2(void *arg)
{
//创建两个文件描述符
int fd_r, fd_w;
//只读打开文件
fd_r = open((*(File_name_t*)arg).str1, O_RDONLY);
if(fd_r < 0)
{
perror("open error");
pthread_exit(NULL);
}
//只写打开文件
fd_w = open((*(File_name_t*)arg).str2, O_WRONLY);
if(fd_w < 0)
{
perror("open error");
close(fd_r);
pthread_exit(NULL);
}
//计算文件大小
off_t file_size = lseek(fd_r, 0, SEEK_END);
//将读写光标移动到文件中间
lseek(fd_r, file_size - file_size/2, SEEK_SET);
lseek(fd_w, file_size - file_size/2, SEEK_SET);
//以每次一个字节进行数据拷贝
char c = 0;
for(int i = file_size - file_size/2; i < file_size; i++)
{
read(fd_r, &c, 1);
write(fd_w, &c, 1);
}
//关闭文件,退出线程
puts("后半部分拷贝完毕");
close(fd_r);
close(fd_w);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int fd_w = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(fd_w < 0)
{
perror("open error");
return -1;
}
close(fd_w);
File_name_t file_name;
file_name.str1 = argv[1];
file_name.str2 = argv[2];
pthread_t tid1, tid2;
if(pthread_create(&tid1, NULL, task1, &file_name) != 0)
{
puts("tid1 pthread_creat error");
return -1;
}
if(pthread_create(&tid2, NULL, task2, &file_name) != 0)
{
puts("tid2 pthread_creat error");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
//三线程拷贝
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
//定义文件结构体
typedef struct file
{
char *strfile;
char *dstfile;
off_t file_size;
off_t file_start;
} File_t;
/*
函数功能:算出文件的大小,若目标文件不存在创建该文件
函数参数:拷贝源文件,拷贝目标文件
返回值:拷贝文件的大小
*/
int get_file_size(const char* strfile, const char* dstfile)
{
//定义返回值和两个文件描述符
int ret = 0;
int fd_w, fd_r;
//以只读的方式打开文件
fd_r = open(strfile, O_RDONLY);
if(fd_r < 0)
{
perror("strfile open error");
return -1;
}
//以只写的形式打开文件
fd_w = open(dstfile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if(fd_w < 0)
{
perror("dstfile open error");
return -1;
}
//计算文件长度
ret = lseek(fd_r, 0, SEEK_END);
//关闭文件
close(fd_w);
close(fd_r);
return ret;
}
/*
函数功能:拷贝文件
函数参数:拷贝源文件,拷贝目标文件,读写文件光标起始位置,拷贝文件的大小
返回值:无
*/
void copy_file(const char* strfile, const char* dstfile, int file_start, int file_size)
{
//定义两个文件描述符
int fd_r, fd_w;
//以只读的方式打开源文件
fd_r = open(strfile, O_RDONLY);
if(fd_r < 0)
{
perror("strfile open error");
return;
}
//以只写的方式打开目标文件
fd_w = open(dstfile, O_WRONLY);
if(fd_w < 0)
{
perror("dstfile open error");
return;
}
//定位光标位置
lseek(fd_r, file_start, SEEK_SET);
lseek(fd_w, file_start, SEEK_SET);
//进行数据拷贝
char c = 0;
int ret = 0;
while(1)
{
if((ret = read(fd_r, &c, 1)) == 0)
break;
write(fd_w, &c, 1);
}
puts("拷贝成功");
}
//三个线程各自拷贝文件的三分之一大小
void *task1(void *arg)
{
File_t f = *(File_t*)arg;
copy_file(f.strfile, f.dstfile, f.file_start, f.file_size);
pthread_exit(NULL);
}
void *task2(void *arg)
{
File_t f = *(File_t*)arg;
copy_file(f.strfile, f.dstfile, f.file_start, f.file_size);
pthread_exit(NULL);
}
void *task3(void *arg)
{
File_t f = *(File_t*)arg;
copy_file(f.strfile, f.dstfile, f.file_start, f.file_size);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
//判断文件传入数量
if(argc != 3)
{
printf("input file error\n");
printf("usage: ./a.out srcfile dstfile\n");
return -1;
}
//定义三个线程tid
pthread_t tid1, tid2, tid3;
//对文件结构体进行赋值
File_t File;
File.strfile = argv[1];
File.dstfile = argv[2];
//task1线程光标的开始位置
File.file_start = 0;
File.file_size = get_file_size(argv[1], argv[2]);
if(pthread_create(&tid1, NULL, task1, &File) != 0)
{
puts("tid1 pthread_create error");
return -1;
}
//task2线程光标的开始位置
File.file_start = get_file_size(argv[1], argv[2])/3;
if(pthread_create(&tid2, NULL, task2, &File) != 0)
{
puts("tid2 pthread_create error");
return -1;
}
//task3线程光标的开始位置
File.file_start = File.file_size - (File.file_size - get_file_size(argv[1], argv[2])/3);
if(pthread_create(&tid3, NULL, task3, &File) != 0)
{
puts("tid3 pthread_create error");
return -1;
}
//线程资源的回收
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
return 0;
}