std:string 사용시 문제
글쓴이: ohdol / 작성시간: 월, 2006/02/13 - 4:28오후
pthread_mutex_t sync_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t sync_cond = PTHREAD_COND_INITIALIZER; vector<string> gsFile; void* th_send(void* data); int loadFile(); int main(int argc, char** argv) { int iThNum = 1; int i, j; if(argc > 1) { iThNum = atoi(argv[1]); } if(!loadFile()) _exit(0); printf("File Load Done \n"); vector<pthread_t> vThreadList(iThNum); for(i=0;i<iThNum;i++) { pthread_mutex_lock(&sync_mutex); pthread_create(&vThreadList[i],NULL,th_send,(void*)&i); pthread_cond_wait(&sync_cond, &sync_mutex); pthread_mutex_unlock(&sync_mutex); } printf("Thread Wait :%d %d\n", vThreadList.size(), vThreadList.capacity()); for(j=0;j<(int)vThreadList.size();j++) { if(!pthread_join(vThreadList[j],NULL)) printf("Thread Join Success \n"); else perror("Thread Join Fail :"); } printf("All Thread End \n"); return 1; } void* th_send(void* data) { int iThIdx = 0; char sBuf[1024+1] = {0x00}; class MYSocket *pclsSock = NULL; vector<string>::iterator mi; pthread_mutex_lock(&sync_mutex); iThIdx = *(int*)data; iThIdx++; printf("Thread Create : [%d]\n", iThIdx); pthread_cond_signal(&sync_cond); pthread_mutex_unlock(&sync_mutex); pclsSock = new class MYSocket; if(pclsSock->makeSocket() <0) { perror("Make Socket Fail :"); pclsSock->closeSocket(); return (void*)NULL; } if(pclsSock->makeConnector() < 0) { perror("Make Connector Fail :"); pclsSock->closeSocket(); return (void*)NULL; } mi = gsFile.begin(); while(mi != gsFile.end()) { memset((void*)sBuf, 0x00, sizeof(sBuf)); sprintf(sBuf, "%s\r\n", mi->c_str()); ===>#1 Segmentation fault sprintf(sBuf, "%s", mi->c_str()); ===>#2 에러 안남 pclsSock->send(sBuf); mi++; } //end of while(mi != gsFile.end()) printf("SendAll %d Thread\n", iThIdx); pclsSock->closeSocket(); delete pclsSock; pclsSock = NULL; printf("Sock Close Done\n"); return (void*)NULL; } int loadFile() { FILE* fp = NULL; if((fp = fopen(_XML_FILE, "r")) == NULL) { perror("File Open Fail :"); return 0; } char sBuf[1024+1] = {0x00}; while(!feof(fp)) { if(fgets(sBuf, sizeof(sBuf), fp) == NULL) break; gsFile.push_back(sBuf); } //end of while(feof(fp)) printf("Vector Size : %d\n", gsFile.size()); return 1; }
위 코드에서 string vector에 있는걸 불러와서 소켓으로 쏴주는 테스트 프로그램 작성 중입니다.
vector에 있는 string을 불러와 그냥 sprintf 또는 strcpy 하면 아무런 문제가 없는데 sprintf 에 문자열을 편집하면 쓰레드 종료하고 Segmentation fault 가 떨어지네요. pthread_join 할때 문제 발생하네요.
std 스트링 처리에서 문제가 있는것 같습니다. 위의 코드에 ===>로 표시한 두 부분을 하나씩 주석처리해서 돌려보면 #1 은 문제가 있고 #2로 처리하면 잘 종료됩니다.
무슨 문제 인지 조언 좀 부탁드립니다~
Forums:
우선....
STL의 모든 컴퍼넌트는 Thread Safe 하지 않습니다. 즉 Read에 대해서만 안전성을 보장합니다. Write를 할 경우 안됩니다.
더구나 std::vector의 경우 push_back이나...그외 여러가지 iterator 에 관련된 operation을 취할 경우 모든 iterator가 invalid 하게 됩니다. list는 그렇지않죠. 멀티스레드와 STL 조합을 잘못 하신것 같네요.
------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/
제가 봐도 위 소스 코드는 문제가 없어 보이는군요.굳이 의심을 하자면
제가 봐도 위 소스 코드는 문제가 없어 보이는군요.
굳이 의심을 하자면 "스트링의 사이즈가 버퍼보다 큰게 아닌가?" 하는 의심이 가지만, 아마 스트링이 1024 사이즈를 넘기지 않는 상황 같구요.
(mi->size() 로 사이즈 체크를 해보시면?)
efence 나 valgrind 같은 걸 사용해 보세요.
메모리 에러는 다른 곳의 잘 못으로, 뒤늦게 나타나는 경우가 많으니깐요.
그리고
여기에 보면 return 하기 전에 pclsSock 을 delete 해야 하는데 안하셨네요.
저기 상황에서는 MYSocket 를 힙에 생성할 필요 없이 그냥 스택에 하면 되는 상황인데 힙에 하신건 비효율적이군요.(new delete 연산은 부하가 큽니다.)
꼭 그렇게 해야 하는 상황이면 boost 라이브러리의 스마트 포인터 중에 scoped_ptr 을 추천합니다. 딱 저런 상황에서 쓰기 좋은 녀석입니다.
[code:1] char sBuf[1024+1] =
여기서 fgets 로 하면 버퍼에 newline 도 버퍼에 저장하는 듯 한데
이것을 string 으로 담을때 잘 못 처리되는게 아닐까요?
fgets 로 읽은후에 sBuf 의 뒷부분에 있는 newline 을 제거한후에
gsFile 에 담아보는 것을 한번 해보세요.
아...이런
아...이론...^^;
테스트 프로그램이라 급하게 작성하느라 sBuf 하나를 여러군데에서 쓰다보니 버퍼가 넘친거네요.
파일 내용이 쭉 이어져 있어서 1024byte 씩 리스트에 넣어둔건 데 거기다 CRLF를 붙이니 에러가 난거네요.
Thread Safe 문제는 좀 더 조사를 해봐야겠네요.
^^ always smile
위의 코드에서는 Thread Safe 문제는 없습니다.STL 자체가
위의 코드에서는 Thread Safe 문제는 없습니다.
STL 자체가 Thread Safe 하지 않지만, 위의 코드처럼 일단 stl 에 write 는 메인에서 다른 쓰레드를 만들기 전에 모두 완료한후에 쓰레드에서는 read 만 했기 때문에 적절한 코드입니다.
[code:1]int loadFile(){
fclose를 안해주신것 같네요! ^^
아.. 그리고..
이렇게 하는것보다는
이런식으로 하시는게 좀 더 안전하지 않을까요? ^^
(mi의 string size가 더 클경우 모두 카피되진 않지만, safe하게 작성된 코드가 아닐까 싶습니다..)
안녕하세요~? 최용진입니다..
printf 류의 함수가 thread-safe한지 아닌지는시스템(라
printf 류의 함수가 thread-safe한지 아닌지는
시스템(라이브러리)에 따라 달라서,
쓰레드 함수에서의 sprintf는 쓰지 않는 게 좋겠읍니다.
얘네들을 글로벌 버퍼로 구현하는 경우가 많습니다.
[quote="Anonymous"]printf 류의 함수가 thread
그럼 대용으로 사용할 수 있는 함수에는 어떤것이 있을까요?
또 thread-safe 여부를 어디서 알수 있죠?
sprintf man 페이지에는 thread에 관한 내용이 없어서...
^^ always smile
리눅스에서는 man 페이지에 쓰래드 안정성에 대한 이야기가 없는 듯 하더
리눅스에서는 man 페이지에 쓰래드 안정성에 대한 이야기가 없는 듯 하더군요.
솔라리스 man 페이지에서 Async-Signal-Safe 나 MT-Safe 에 대한 이야기를 해주더군요.
"sprintf 가 쓰레드에 안전하지 않은 라이브러리가 있다." 라는 사실에 전 부정적으로 생각합니다.
버퍼를 내가 넘겨주는데 sprintf 가 글로벌 버퍼를 왜 사용하죠?
최소한, 솔라리스에서는 printf, sprintf 는 MT-Safe with exceptions 입니다.
근데 여기서 with exceptions 이 무슨 의미인지는 모르겠습니다.
exception 이 발생해도 안정적인라는 이야기 일까요?
ISO C 표준에 따르면, C 표준 라이브러리가 thread-safe할
ISO C 표준에 따르면, C 표준 라이브러리가 thread-safe할 의무가 없습니다.
사실 thread에 대한 언급은 없으며, 정확히 "reentrant"할 의무가 없습니다.
하지만, POSIX.1c (SUS Ver 3.0)를 준수하는 시스템에서 제공하는 C 라이브러리 함수는 특별히 언급할 경우를 제외하고, 모두 thread-safe합니다. 따라서 Linux man page에 특별한 언급이 없을 경우, thread-safe로 여겨도 무방합니다.
보통 함수 xxx()가 thread-safe하지 않을 경우, xxx_r()이라는 thread-safe한 함수를 제공합니다.
결국 sprintf()는 thread-safe한 것이 됩니다.
또, POSIX.1c에 의해 stream I/O 관련 함수(예: getchar(), fgets(), fgetc() 등등)도 모두 thread-safe합니다. 즉, 여러 thread에서 동시에 한 파일을 읽더라도 호출된 순서대로 불려집니다. 하지만, 이 경우, 한 thread에서 여러 번 I/O 함수를 호출하게 되고, 이것이 atomic하게 이루어져야 한다면, flockfile()로 스트림 자체에 lock을 걸 수 있습니다.
이 모든 것은 glibc (GNU C library)를 쓸 때 다 적용됩니다. (오래된 libc5와 같은 라이브러리는 atomic한 stream function을 제공하지 않습니다. 따라서 libc5의 stream I/O 함수들이 glibc보다 더 빠릅니다.) 하지만 POSIX.1c 표준도 getc_unlocked()와 같이, atomic하지 않은, 더 빠른 함수를 제공하므로, 궂이 libc5를 쓸 필요는 없습니다.
glibc를 쓰는 대부분의 시스템에서 thread를 써서 프로그램을 개발한다면, glibc의 헤더들을 포함시키기 전 (보통 컴파일러 -D 옵션을 써서 처리), 매크로_REENTRANT나 _THREAD_SAFE를 정의하면, thread-safe한 함수들을 쓰도록 강제합니다.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
Korean Ver: http://cinsk.github.io/cfaqs/
댓글 달기