前一段排查產品的一個異常問題,是有關C++ string類的,做字符串賦值操作時,應用程序崩潰了。
堆棧信息如下:
經過分析代碼,發現同一個字符串變量會在多個線程里做賦值操作,而且沒有加鎖保護。
string類字符串賦值操作是線程不安全的,當多個線程同時對同一個string類變量做賦值操作時,就會產生異常。
一般來說,聲明string變量時(不做賦值,或者字符串長度為0),string對象內會有一個保存字符串的地址,其內存長度為0;
當再次賦值一個較長字符串時,string對象會重新從堆中分配一段地址存放內容,釋放之前聲明時產生的內存地址。
先看一個簡單的string 初始化例子:
1 #include <string> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdio.h> 5 6 using std::string; 7 8 int main(int argc, const char* argv[]) 9 { 10 string localstr=""; 11 if(localstr.empty()) 12 printf("localstr empty: %s\n", localstr.c_str()); 13 14 15 printf("c_str = %p, capacity = %d, size = %d\n", localstr.c_str(), localstr.capacity(), localstr.size()); 16 localstr = "hello world!"; 17 printf("c_str = %p, capacity = %d, size = %d\n", localstr.c_str(), localstr.capacity(), localstr.size()); 18 19 return 0; 20 }
打印輸出:
1 c_str = 0x602028, capacity = 5, size = 5 2 c_str = 0x602058, capacity = 12, size = 12
如果是多個線程同時對同一個string對象做賦值操作,在沒有任何鎖保護情況下,會產生異常。
示例代碼核心部分:
1 string test_str1; 2 string test_str2; 3 4 void *string_test_thread_ex(void *p) 5 { 6 struct sched_param curr_param; 7 8 prctl(PR_SET_NAME, "string_test"); 9 10 11 NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF); 12 if(NUM_PROCS == -1) 13 { 14 printf("failed to get cpu nums: %s(%d)\n", strerror(errno), errno); 15 NUM_PROCS = DEF_CPU_NUM; 16 } 17 18 CPU_ZERO(&cpuset); 19 cpuidx = (curnum++)%NUM_PROCS; 20 CPU_SET(cpuidx, &cpuset); 21 if (pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset)) 22 { 23 perror("pthread_setaffinity_np"); 24 } 25 while(1) 26 { 27 test_str1 = "hello world1"; 28 test_str2 = "hello world2"; 29 30 usleep(50); 31 } 32 33 }
運行結果:
1 now create the thread: 2 *** glibc detected *** ./stringtest: double free or corruption (fasttop): 0x00007f8de80009f0 *** 3 ======= Backtrace: ========= 4 /lib64/libc.so.6(+0x73ee8)[0x7f8dfe281ee8] 5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 6 /lib64/libc.so.6(cfree+0x6c)[0x7f8dfe286d6c] 7 /usr/lib64/libstdc++.so.6(_ZNSs9_M_mutateEmmm+0x1c3)[0x7f8dfeaf2a03] 8 /usr/lib64/libstdc++.so.6(_ZNSs15_M_replace_safeEmmPKcm+0x2c)[0x7f8dfeaf2a3c] 9 ./stringtest[0x40237a] 10 /lib64/libpthread.so.0(+0x79ed)[0x7f8dfef5b9ed] 11 /lib64/libc.so.6(clone+0x6d)[0x7f8dfe2ea14d]
異常堆棧信息和本文開頭的不一樣,但都是由string對象的並發操作導致APP運行異常。
參考資料:
https://my.oschina.net/u/4000302/blog/4469522
https://blog.csdn.net/cny901111/article/details/7771668
您的支持是對博主最大的鼓勵👍,感謝您的認真閱讀。
本博客持續更新,歡迎您關注和交流!
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。