忘记密码   免费注册 查看新帖 | 论坛精华区

ChinaUnix.net

  平台 论坛 博客 认证专区 大话IT HPC论坛 徽章 文库 沙龙 自测 下载 频道自动化运维 虚拟化 储存备份 C/C++ PHP MySQL 嵌入式 Linux系统
12下一页
最近访问板块 发新帖
查看: 781 | 回复: 10

[C] 求助,多线程写入文件错乱问题!!~~ [复制链接]

论坛徽章:
0
发表于 2017-11-06 16:36 |显示全部楼层
自己练习,多线程复制文件,同一文件下,多线程并发写入同一文件的不同部分。
思路是,提前为每个线程分配好写入内容大小,每个线程执行fopen获取单独的文件描述符,然后按分配的写入大小,fseek到不同的位置,并发写入内容。

但是写入的内容总是错乱。

  1. #include <stdio.h>
  2. #include <sys/stat.h>
  3. #include <string.h>
  4. #include <pthread.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>

  7. #define BUFF_SIZE 512
  8. #define PTHREAD_NUMBER 4

  9. typedef struct copy_block
  10. {
  11. char fin[BUFF_SIZE];
  12. char fout[BUFF_SIZE];
  13. long start;        //起始位置
  14. long segment_size; //分段大小
  15. int id;        //虚拟线程id
  16. } __attribute__((packed)) page;

  17. long file_size(char *filename)
  18. {
  19. struct stat fstat;
  20. memset(&fstat, 0, sizeof(fstat));
  21. stat(filename, &fstat);
  22. return fstat.st_size;
  23. }

  24. //单线程任务逻辑
  25. void pthread_copy(void *arg)
  26. {
  27. //转换指针类型
  28. page *p = (page *)arg;

  29. //每个线程单独打开文件
  30. FILE *fin = fopen(p->fin, "r");
  31. FILE *fout = fopen(p->fout, "wb+");

  32. //移动流到偏移位置
  33. int res1 = fseek(fin, p->start, SEEK_SET);
  34. int res2 = fseek(fout, p->start, SEEK_SET);

  35. //开始复制
  36. char buffer[BUFF_SIZE];        //读写区
  37. long read_size = BUFF_SIZE; //预设读写大小
  38. long left = p->segment_size; //剩余大小,初始化为任务总大小
  39. long reade_len = 0;        //读取文件大小
  40. long total_len = 0;

  41. //当剩余大小大于0时保持复制
  42. while (left > 0)
  43. {
  44. //如果文件剩余大小小于预设读写大小,则按剩余大小读取
  45. if (read_size > left)
  46. {
  47. read_size = left;
  48. }
  49. //读取文件
  50. reade_len = fread(buffer, 1, read_size, fin);
  51. total_len += reade_len;
  52. //写入文件
  53. if (reade_len > 0)
  54. {
  55. fwrite(buffer, 1, reade_len, fout);
  56. }

  57. //剩余大小减去已读写大小
  58. left = left - reade_len;
  59. }

  60. //复制完成关闭文件
  61. fclose(fin);
  62. fclose(fout);
  63. pthread_exit(NULL);
  64. }

  65. //开启多线程任务
  66. int multi_copy(char *src, char *dest)
  67. {
  68. //判断文件是否存在,以及是否具有读取权限
  69. int file_exist = access(src, 4);
  70. if (file_exist != 0)
  71. fprintf(stderr, "源文件不存在");

  72. //获取文件大小
  73. long fsize = file_size(src);

  74. //真正运行线程数量
  75. int real_pthread_number = PTHREAD_NUMBER;
  76. if (fsize < PTHREAD_NUMBER)
  77. real_pthread_number = 1;

  78. //给任务结构体分配内存
  79. page *p;
  80. p = malloc(sizeof(*p) * PTHREAD_NUMBER);

  81. long offset = 0;        //文本偏移量
  82. long segment = fsize / real_pthread_number;        //分段长度
  83. long segment_remainder = fsize % real_pthread_number; //分段后剩余长度

  84. //给每个线程分配任务
  85. for (int i = 0; i < real_pthread_number; i++)
  86. {
  87. //分配复制任务的文件大小
  88. if (i + 1 == real_pthread_number)
  89. {
  90. p[i].segment_size = segment + segment_remainder;
  91. }
  92. else
  93. {
  94. p[i].segment_size = segment;
  95. }

  96. //确定任务的起止位置
  97. p[i].start = offset;
  98. offset = offset + p[i].segment_size;

  99. //文件路径存入任务
  100. strncpy(p[i].fin, src, strlen(src));
  101. strncpy(p[i].fout, dest, strlen(dest));

  102. //分配虚拟线程id
  103. p[i].id = i;
  104. }

  105. //创建线程
  106. pthread_t work[real_pthread_number];
  107. for (int i = 0; i < real_pthread_number; i++)
  108. {
  109. pthread_create(&work[i], NULL, (void *)&pthread_copy, (void *)&p[i]);
  110. }

  111. //阻塞主线程
  112. for (int i = 0; i < real_pthread_number; i++)
  113. {
  114. pthread_join(work[i], NULL);
  115. }

  116. //释放任务结构体占用内存
  117. if (p != NULL)
  118. {
  119. free(p);
  120. p = NULL;
  121. }

  122. return 0;
  123. }

  124. int main(int argc, char *argv[])
  125. {
  126. char *src;
  127. char *dest;

  128. src = argv[1];
  129. dest = argv[2];

  130. multi_copy(src, dest);
  131. }
复制代码


论坛徽章:
243
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2017-11-07 08:52 |显示全部楼层
“wb+”都把目标文件清空了

论坛徽章:
0
发表于 2017-11-07 09:35 |显示全部楼层
回复 2# hellioncu

用过 w+,wb,wb+都有问题,不知道应该用哪种模式?

论坛徽章:
243
射手座
日期:2013-08-23 12:04:38射手座
日期:2013-08-23 16:18:12未羊
日期:2013-08-30 14:33:15水瓶座
日期:2013-09-02 16:44:31摩羯座
日期:2013-09-25 09:33:52双子座
日期:2013-09-26 12:21:10金牛座
日期:2013-10-14 09:08:49申猴
日期:2013-10-16 13:09:43子鼠
日期:2013-10-17 23:23:19射手座
日期:2013-10-18 13:00:27金牛座
日期:2013-10-18 15:47:57午马
日期:2013-10-18 21:43:38
发表于 2017-11-07 09:43 |显示全部楼层
zhjphp 发表于 2017-11-07 09:35
回复 2# hellioncu

用过 w+,wb,wb+都有问题,不知道应该用哪种模式?

你在主线程先把目标文件创建好,这样好处理

论坛徽章:
0
发表于 2017-11-07 09:54 |显示全部楼层
回复 4# hellioncu

非常感谢,主线程创建文件后,子线程使用rb+打开文件,写入成功~~
明白了~

论坛徽章:
14
射手座
日期:2014-11-29 19:22:49黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:252015年迎新春徽章
日期:2015-03-04 09:50:28
发表于 2017-11-07 15:28 |显示全部楼层
zhjphp 发表于 2017-11-07 09:35
回复 2# hellioncu

用过 w+,wb,wb+都有问题,不知道应该用哪种模式?

w+r

写的时候加锁。

论坛徽章:
0
发表于 2017-11-08 08:50 |显示全部楼层
如果频繁写文件,一定是先写在缓冲区,定时或关闭时flush到文件里。
为什么要多线程写文件? IO读写最影响性能

论坛徽章:
14
射手座
日期:2014-11-29 19:22:49黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:252015年迎新春徽章
日期:2015-03-04 09:50:28
发表于 2017-11-08 15:09 |显示全部楼层
sxcong 发表于 2017-11-08 08:50
如果频繁写文件,一定是先写在缓冲区,定时或关闭时flush到文件里。
为什么要多线程写文件? IO读写最影响 ...

说得对,单纯读写文件,多线程并无意义。

论坛徽章:
0
发表于 2017-11-14 14:58 |显示全部楼层
回复 6# yulihua49

加锁的话那并发就没多大意义了

论坛徽章:
35
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
发表于 2017-11-14 18:01 |显示全部楼层
复制文件用splice
您需要登录后才可以回帖 登录 | 注册

本版积分规则

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号 北京市公安局海淀分局网监中心备案编号:11010802020122
广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员  联系我们:
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP