免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5136 | 回复: 10
打印 上一主题 下一主题

[求助]字符串连接thread unsafe [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-06-06 13:40 |只看该作者 |倒序浏览
偶自己写的字符串连接造成多线程不安全

字符串连接头文件:join.h

  1. #ifndef JOIN_H

  2. char *Join(char *JoinItems[]);

  3. #endif
复制代码


字符串连接:join.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. char *
  5. Join(char *JoinItems[])
  6. {
  7.         char *JoinStr                 = NULL;
  8.         size_t JoinStrLen = 0;
  9.         int TotalItemsNum = 0;
  10.         int i;

  11.         while (*JoinItems != NULL)
  12.         {
  13.                 JoinStrLen += strlen(*JoinItems);
  14.                 JoinItems++;
  15.                 TotalItemsNum++;
  16.         }

  17.         if ((JoinStr = malloc(JoinStrLen)) != NULL)
  18.         {
  19.                 for (i = 0; i < TotalItemsNum; i++)
  20.                 {
  21.                         JoinItems--;
  22.                 }
  23.                 while (*JoinItems != NULL)
  24.                 {
  25.                         strcat(JoinStr, *JoinItems);
  26.                         JoinItems++;
  27.                 }
  28.        
  29.                 return JoinStr;
  30.         }
  31.         else
  32.         {
  33.                 return "FALSE";
  34.         }
  35. }

复制代码


单线程测试用例:testjoin.c,单线程工作起来是正常的

  1. #include <stdio.h>
  2. #include "join.h"

  3. int
  4. main(int argc, char *argv[])
  5. {
  6.         char *JoinStr = NULL;
  7.         char *items[] = {
  8.                 "./cache/",
  9.                 "sirtoozee",
  10.                 ".html",
  11.                 NULL
  12.         };
  13.        
  14.         if (argc < 2)
  15.         {
  16.                 JoinStr = Join(items);
  17.         }
  18.         else
  19.         {
  20.                 JoinStr = Join(argv);
  21.         }
  22.         printf("%s\n", JoinStr);
  23.         free(JoinStr);
  24.         JoinStr = NULL;
  25.        
  26.         return 0;
  27. }

复制代码


多线程测试用例:testmtjoin.c,工作起来就不正常了

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include "join.h"
  4. #include "./../md5/md5.h"

  5. #define DIR "./cache/"
  6. #define EXT ".html"

  7. char *urls[] = {
  8.         "http://localhost/",
  9.         "http://localhost/yui/",
  10.         "http://localhost/libc/"
  11. };

  12. void *TestCallback(void *url)
  13. {
  14.         FILE *fw;
  15.         char *cache_file;
  16.         char *JoinItems[] = {
  17.                 DIR,
  18.                 SIR_MD5(url),
  19.                 EXT,
  20.                 NULL
  21.         };
  22.        
  23.         if ((cache_file = Join(JoinItems)) != "FALSE")
  24.         {
  25.                 if ((fw = fopen(cache_file, "w")) != NULL)
  26.                 {
  27.                         fputs(url, fw);
  28.                         printf("Constructed %s\n", cache_file);
  29.                 }
  30.                 else
  31.                 {
  32.                         printf("Fail to wrtie to %s\n", cache_file);
  33.                 }
  34.         }
  35.         else
  36.         {
  37.                 printf("Fail to join %s\n", cache_file);
  38.         }
  39.         fclose(fw);
  40.         free(cache_file);
  41.         cache_file = NULL;
  42.         free(JoinItems);

  43.         return NULL;
  44. }

  45. int
  46. main(int argc, char *argv[])
  47. {
  48.         pthread_t tid[3];
  49.         int i;
  50.        
  51.         for (i = 0; i < 3; i++)
  52.         {
  53.                 pthread_create(&tid[i], NULL, TestCallback, urls[i]);
  54.         }
  55.        
  56.         for (i = 0; i < 3; i++)
  57.         {
  58.                 pthread_join(tid[i], NULL);
  59.                 printf("Destructed %s.\n", urls[i]);
  60.         }
  61.        
  62.         return 0;
  63. }

复制代码


由于使用了openssl的md5加密,提贴出代码:

  1. #ifndef MD5_H

  2. char *SIR_MD5(char *str);

  3. #endif

  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <openssl/evp.h>
  7. #include <openssl/md5.h>

  8. static char *pt(unsigned char *md);

  9. static char
  10. *pt(unsigned char *md)
  11. {
  12.   int i;
  13.   static char buf[80];

  14.   for (i = 0; i < MD5_DIGEST_LENGTH; i++)
  15.   {
  16.     sprintf(&(buf[i * 2]), "%02x", md[i]);
  17.   }
  18.   return(buf);
  19. }

  20. char *SIR_MD5(char *str)
  21. {
  22.   char *p;
  23.   unsigned char md[MD5_DIGEST_LENGTH];
  24.        
  25.   EVP_Digest(str, strlen(str), md, NULL, EVP_md5(), NULL);
  26.   p = pt(md);
  27.        
  28.   return p;
  29. }

复制代码


我的SConstruct脚本:

  1. Program('testmtjoin', Split('./../md5/md5.c join.c testmtjoin.c'), LIBS=['-lssl', '-lpthread'])
  2. #Program('testjoin', Split('join.c testjoin.c'))
复制代码


错误信息:

  1. constructed ./cache/c9db569cb388e160e4b86ca1ddff84d7.html
  2. *** glibc detected *** ./testmtjoin: double free or corruption (out): 0xb7cbf450 ***
  3. ======= Backtrace: =========
  4. /lib/libc.so.6[0xb7e2dcb0]
  5. /lib/libc.so.6(__libc_free+0x84)[0xb7e2f2f4]
  6. ./testmtjoin[0x804898f]
  7. /lib/libpthread.so.0[0xb7eee294]
  8. /lib/libc.so.6(__clone+0x5e)[0xb7e8632e]
  9. ======= Memory map: ========
  10. 08048000-08049000 r-xp 00000000 08:03 2959737    /root/Projects/sirch/trunk/tests/join/testmtjoin
  11. 08049000-0804a000 rw-p 00000000 08:03 2959737    /root/Projects/sirch/trunk/tests/join/testmtjoin
  12. 0804a000-0806b000 rw-p 0804a000 00:00 0          [heap]
  13. b6300000-b6321000 rw-p b6300000 00:00 0
  14. b6321000-b6400000 ---p b6321000 00:00 0
  15. b64bd000-b64be000 ---p b64bd000 00:00 0
  16. b64be000-b6cbe000 rw-p b64be000 00:00 0
  17. b6cbe000-b6cbf000 ---p b6cbe000 00:00 0
  18. b6cbf000-b74bf000 rw-p b6cbf000 00:00 0
  19. b74bf000-b74c0000 ---p b74bf000 00:00 0
  20. b74c0000-b7cc1000 rw-p b74c0000 00:00 0
  21. b7cc1000-b7cc3000 r-xp 00000000 08:03 1733411    /lib/libdl-2.4.so
  22. b7cc3000-b7cc5000 rw-p 00001000 08:03 1733411    /lib/libdl-2.4.so
  23. b7cc5000-b7cc6000 rw-p b7cc5000 00:00 0
  24. b7cc6000-b7db6000 r-xp 00000000 08:03 1116342    /usr/lib/libcrypto.so.0.9.7
  25. b7db6000-b7dc8000 rw-p 000f0000 08:03 1116342    /usr/lib/libcrypto.so.0.9.7
  26. b7dc8000-b7dcb000 rw-p b7dc8000 00:00 0
  27. b7dcb000-b7ee2000 r-xp 00000000 08:03 1733408    /lib/libc-2.4.so
  28. b7ee2000-b7ee4000 r--p 00116000 08:03 1733408    /lib/libc-2.4.so
  29. b7ee4000-b7ee6000 rw-p 00118000 08:03 1733408    /lib/libc-2.4.so
  30. b7ee6000-b7ee9000 rw-p b7ee6000 00:00 0
  31. b7ee9000-b7ef8000 r-xp 00000000 08:03 1733424    /lib/libpthread-2.4.so
  32. b7ef8000-b7ef9000 r--p 0000e000 08:03 1733424    /lib/libpthread-2.4.so
  33. b7ef9000-b7efa000 rw-p 0000f000 08:03 1733424    /lib/libpthread-2.4.so
  34. b7efa000-b7efc000 rw-p b7efa000 00:00 0
  35. b7efc000-b7f2a000 r-xp 00000000 08:03 1116339    /usr/lib/libssl.so.0.9.7
  36. b7f2a000-b7f2d000 rw-p 0002e000 08:03 1116339    /usr/lib/libssl.so.0.9.7
  37. b7f3d000-b7f47000 r-xp 00000000 08:03 1115354    /usr/lib/gcc/i686-pc-linux-gnu/4.1.1/libgcc_s.so.1
  38. b7f47000-b7f48000 rw-p 00009000 08:03 1115354    /usr/lib/gcc/i686-pc-linux-gnu/4.1.1/libgcc_s.so.1
  39. b7f48000-b7f49000 rw-p b7f48000 00:00 0
  40. b7f4a000-b7f4b000 rw-p b7f4a000 00:00 0
  41. b7f4b000-b7f4c000 r-xp b7f4b000 00:00 0          [vdso]
  42. b7f4c000-b7f66000 r-xp 00000000 08:03 1733396    /lib/ld-2.4.so
  43. b7f66000-b7f67000 r--p 00019000 08:03 1733396    /lib/ld-2.4.so
  44. b7f67000-b7f68000 rw-p 0001a000 08:03 1733396    /lib/ld-2.4.so
  45. bfd06000-bfd1b000 rw-p bfd06000 00:00 0          [stack]
复制代码


我的系统是Gentoo,gcc是4.1.1,多线程操作的时候,malloc是不是需要“特别照顾”呢?请大家帮帮sirtoozee,谢谢了

论坛徽章:
0
2 [报告]
发表于 2007-06-06 13:51 |只看该作者

回复 1楼 涩兔子 的帖子

malloc()函数是不可重入的函数吧.

论坛徽章:
0
3 [报告]
发表于 2007-06-06 14:02 |只看该作者
记得malloc是线程安全的。
为什么要
free(JoinItems);

论坛徽章:
0
4 [报告]
发表于 2007-06-06 14:23 |只看该作者
跟malloc无关.


  1. if ((cache_file = Join(JoinItems)) != "FALSE")
复制代码

字符串能这样比较吗?


  1. free(JoinItems);
复制代码

没找到这个JoinItems在哪里用malloc申请了空间?


  1. static char buf[80];
复制代码

相当于多个线程修改一个全局变量但不加锁.

论坛徽章:
0
5 [报告]
发表于 2007-06-07 10:05 |只看该作者

谢谢coldwarm的提示,问题解决了^_^

+ 了一个mutex


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <pthread.h>

  5. char *
  6. Join(char *JoinItems[])
  7. {
  8.         char *JoinStr                 = NULL;
  9.         size_t JoinStrLen = 0;
  10.         int TotalItemsNum = 0;
  11.         int i;
  12.         pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER;
  13.         int rc;

  14.         while (*JoinItems != NULL)
  15.         {
  16.                 JoinStrLen += strlen(*JoinItems);
  17.                 JoinItems++;
  18.                 TotalItemsNum++;
  19.         }
  20.         rc = pthread_mutex_lock(&a_mutex);
  21.         if ((JoinStr = malloc(JoinStrLen)) != NULL)
  22.         {
  23.                 for (i = 0; i < TotalItemsNum; i++)
  24.                 {
  25.                         JoinItems--;
  26.                 }
  27.                 while (*JoinItems != NULL)
  28.                 {
  29.                         strcat(JoinStr, *JoinItems);
  30.                         JoinItems++;
  31.                 }
  32.                 rc = pthread_mutex_unlock(&a_mutex);
  33.                
  34.                 return JoinStr;
  35.         }
  36.         else
  37.         {
  38.                 rc = pthread_mutex_unlock(&a_mutex);

  39.                 return "JOIN_FAIL";
  40.         }
  41. }

复制代码


就像coldwarm提示的,修改了share memory的东东,居然不加锁

还有,字符串的比较那个是比较寒,看出错时的返回值return "JOIN_FAIL";

JoinItems确实木有申请空间,思维那个混乱呀

哦,如何看单个进程所能承担的最大线程数目呀?


  1. #include <stdio.h>
  2. #include <pthread.h>
  3. /* When created 384 threads, it meets the segment fault. */
  4. #define MAX 384

  5. void *
  6. _Construct(void *i)
  7. {
  8.         printf("Constructed %d thread.\n", (int) i);

  9.         return NULL;
  10. }

  11. int
  12. main(int argc, char *argv[])
  13. {
  14.         pthread_t tid[MAX];
  15.         int i;
  16.        
  17.         for (i = 0; i < MAX; i++)
  18.         {
  19.                 pthread_create(&tid[i], NULL, _Construct, (void *) i);
  20.         }

  21.         for (i = 0; i < MAX; i++)
  22.         {
  23.                 pthread_join(tid[i], NULL);
  24.                 printf("Destructed %d thread.\n", i);
  25.         }
  26.        
  27.         return 0;
  28. }

复制代码


我到384个就段错误了,是不是就是极限是384呢?请大家给些提示咯,谢谢

论坛徽章:
0
6 [报告]
发表于 2007-06-07 10:25 |只看该作者
请判断一下线程是否成功创建

论坛徽章:
0
7 [报告]
发表于 2007-06-07 10:39 |只看该作者
/usr/include/bits/local_lim.h
这个文件中定义了PTHREAD_THREADS_MAX

论坛徽章:
0
8 [报告]
发表于 2007-06-07 10:52 |只看该作者

嗯,那么当需要处理的条目大于最大线程数目限制,如何解决呢?

根据提示,修改好了,嗯,那么当需要处理的条目大于最大线程数目限制,如何解决呢


  1. #include <stdio.h>
  2. #include <bits/local_lim.h>
  3. #include <pthread.h>

  4. void *
  5. _Construct(void *i)
  6. {
  7.         printf("Constructed %d thread.\n", (int) i);

  8.         return NULL;
  9. }

  10. int
  11. main(int argc, char *argv[])
  12. {
  13.         pthread_t tid[_POSIX_THREAD_THREADS_MAX];
  14.         int i;
  15.        
  16.         for (i = 0; i < _POSIX_THREAD_THREADS_MAX; i++)
  17.         {
  18.                 pthread_create(&tid[i], NULL, _Construct, (void *) (i + 1));
  19.         }

  20.         for (i = 0; i < _POSIX_THREAD_THREADS_MAX; i++)
  21.         {
  22.                 pthread_join(tid[i], NULL);
  23.                 printf("Destructed %d thread.\n", i + 1);
  24.         }
  25.        
  26.         return 0;
  27. }

复制代码

论坛徽章:
0
9 [报告]
发表于 2007-06-07 15:49 |只看该作者
指令cat  /proc/sys/kernel/threads-max显示系统线程数目限制.

进程内每个线程都必须分配一些资源,比如每个线程都有其独立的栈空间--都有一个默认的尺寸,一个进程地址空间的大小是有限的,创建新的线程时,系统需要为新创建的线程保留一段地址空间供线程的战使用,所以可创建的线程数目有限制.

要创建更多的线程,一种办法是减少线程堆栈的尺寸,在创建线程之前用pthread_attr_setstacksize减少线程栈的尺寸.另一种办法是获得更大的地址空间,可以通过换64位机器或者通过增加物理内存并重新编译内核启用PAE支持.

另外,多线程针对I/O密集型的程序可以提高系统的吞吐量,而对CPU密集型的程序根本无益.

论坛徽章:
0
10 [报告]
发表于 2007-06-07 16:35 |只看该作者

谢谢coldwarm的耐心^_^

一直都是简单地看看POSIX的pthread的文档,第一次动手遇到了很多问题,谢谢大家的帮助哈

嗯,我的实现是通过curl的自定义的writeCallBack函数把海量的URL Linked List里的项目抓取到本地,应该符合I/O密集型的程序

多线程下载软件是如何合理地把N个transfer分配到有限的线程里的呢?我Google了有关axel(我的make.conf里用它emerge东东),可是有人说它不是多线程的,是单线程多连接

请高手们继续指点偶咯
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP