std:string 사용시 문제

ohdol의 이미지

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로 처리하면 잘 종료됩니다.
무슨 문제 인지 조언 좀 부탁드립니다~

kalstein의 이미지

STL의 모든 컴퍼넌트는 Thread Safe 하지 않습니다. 즉 Read에 대해서만 안전성을 보장합니다. Write를 할 경우 안됩니다.
더구나 std::vector의 경우 push_back이나...그외 여러가지 iterator 에 관련된 operation을 취할 경우 모든 iterator가 invalid 하게 됩니다. list는 그렇지않죠. 멀티스레드와 STL 조합을 잘못 하신것 같네요.


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

ssehoony의 이미지

제가 봐도 위 소스 코드는 문제가 없어 보이는군요.
굳이 의심을 하자면 "스트링의 사이즈가 버퍼보다 큰게 아닌가?" 하는 의심이 가지만, 아마 스트링이 1024 사이즈를 넘기지 않는 상황 같구요.
(mi->size() 로 사이즈 체크를 해보시면?)

efence 나 valgrind 같은 걸 사용해 보세요.
메모리 에러는 다른 곳의 잘 못으로, 뒤늦게 나타나는 경우가 많으니깐요.

그리고

Quote:
pclsSock = new class MYSocket;
if(pclsSock->makeSocket() <0)
{
perror("Make Socket Fail :");
pclsSock->closeSocket();
return (void*)NULL;
}

여기에 보면 return 하기 전에 pclsSock 을 delete 해야 하는데 안하셨네요.
저기 상황에서는 MYSocket 를 힙에 생성할 필요 없이 그냥 스택에 하면 되는 상황인데 힙에 하신건 비효율적이군요.(new delete 연산은 부하가 큽니다.)
꼭 그렇게 해야 하는 상황이면 boost 라이브러리의 스마트 포인터 중에 scoped_ptr 을 추천합니다. 딱 저런 상황에서 쓰기 좋은 녀석입니다.
ssehoony의 이미지

    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()); 

여기서 fgets 로 하면 버퍼에 newline 도 버퍼에 저장하는 듯 한데
이것을 string 으로 담을때 잘 못 처리되는게 아닐까요?
fgets 로 읽은후에 sBuf 의 뒷부분에 있는 newline 을 제거한후에
gsFile 에 담아보는 것을 한번 해보세요.
ohdol의 이미지

ssehoony wrote:
    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()); 

여기서 fgets 로 하면 버퍼에 newline 도 버퍼에 저장하는 듯 한데
이것을 string 으로 담을때 잘 못 처리되는게 아닐까요?
fgets 로 읽은후에 sBuf 의 뒷부분에 있는 newline 을 제거한후에
gsFile 에 담아보는 것을 한번 해보세요.

아...이론...^^;
테스트 프로그램이라 급하게 작성하느라 sBuf 하나를 여러군데에서 쓰다보니 버퍼가 넘친거네요.
파일 내용이 쭉 이어져 있어서 1024byte 씩 리스트에 넣어둔건 데 거기다 CRLF를 붙이니 에러가 난거네요.

Thread Safe 문제는 좀 더 조사를 해봐야겠네요.

^^ always smile

ssehoony의 이미지

위의 코드에서는 Thread Safe 문제는 없습니다.
STL 자체가 Thread Safe 하지 않지만, 위의 코드처럼 일단 stl 에 write 는 메인에서 다른 쓰레드를 만들기 전에 모두 완료한후에 쓰레드에서는 read 만 했기 때문에 적절한 코드입니다.

c531110의 이미지

int loadFile()
{
    FILE* fp = NULL; 
    ~~~

   fclose(fp);
}

fclose를 안해주신것 같네요! ^^

아.. 그리고..

sprintf(sBuf, "%s\r\n", mi->c_str());

이렇게 하는것보다는
strncpy(sBuf, mi->c_str(), sizeof(sBuf)); 

이런식으로 하시는게 좀 더 안전하지 않을까요? ^^
(mi의 string size가 더 클경우 모두 카피되진 않지만, safe하게 작성된 코드가 아닐까 싶습니다..)

안녕하세요~? 최용진입니다..

익명 사용자의 이미지

printf 류의 함수가 thread-safe한지 아닌지는
시스템(라이브러리)에 따라 달라서,
쓰레드 함수에서의 sprintf는 쓰지 않는 게 좋겠읍니다.
얘네들을 글로벌 버퍼로 구현하는 경우가 많습니다.

ohdol의 이미지

Anonymous wrote:
printf 류의 함수가 thread-safe한지 아닌지는
시스템(라이브러리)에 따라 달라서,
쓰레드 함수에서의 sprintf는 쓰지 않는 게 좋겠읍니다.
얘네들을 글로벌 버퍼로 구현하는 경우가 많습니다.

그럼 대용으로 사용할 수 있는 함수에는 어떤것이 있을까요?

또 thread-safe 여부를 어디서 알수 있죠?

sprintf man 페이지에는 thread에 관한 내용이 없어서...

^^ always smile

ssehoony의 이미지

리눅스에서는 man 페이지에 쓰래드 안정성에 대한 이야기가 없는 듯 하더군요.
솔라리스 man 페이지에서 Async-Signal-Safe 나 MT-Safe 에 대한 이야기를 해주더군요.

"sprintf 가 쓰레드에 안전하지 않은 라이브러리가 있다." 라는 사실에 전 부정적으로 생각합니다.
버퍼를 내가 넘겨주는데 sprintf 가 글로벌 버퍼를 왜 사용하죠?

최소한, 솔라리스에서는 printf, sprintf 는 MT-Safe with exceptions 입니다.
근데 여기서 with exceptions 이 무슨 의미인지는 모르겠습니다.
exception 이 발생해도 안정적인라는 이야기 일까요?

cinsk의 이미지

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한 함수들을 쓰도록 강제합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.