안녕하세요, 시스템 프로그래밍 과목 시험문제인데 전문가 분들의 도움이 필요합니다...

leejk9592의 이미지

#include "apue.h"
#include <pthread.h>
 
int count=0;
pthread_t ntid[8];
//////////////////////////////////////////////////////////////////
void * thr_fn(void *arg)
{
        int i;
 
        for (i=0; i<10000; i++)
                count++;
 
        return((void *)count);
}
 
int main(void)
{
        int i;
        void* res;
        for (i=0; i<2; i++)
                pthread_create(&ntid[i], NULL, thr_fn, NULL);
        for (i=0; i<2; i++)
        {
                pthread_join(ntid[i], &res);
                printf("count%d= %d\n", i, (int)res);
        }
 
        exit(0);
 
        return 0;
}

위 코드의 실행 결과가
count0= 20000
count1= 10000

이렇게 나오는데 왜 이런지 이해가 잘 가지 않습니다....

리눅스 전문가들이 많이 계시다고 해서 올려봅니다
작은 지식을 나눠 주신다면 정말 감사드리겠습니다.

shint의 이미지

- 전역변수 일 경우. 참조한 res 의 포인터 주소' 가 count 의 주소와 같습니다.
- 스레드 마다. count = 0; 으로 초기화 하시면. 값이 또 달라집니다.
- int i=0; 은 지역변수 입니다.
- 변수 이름과 스레드 이름을 별도로 생성해서 사용하시면. 중복 문제는 적어집니다.

전역변수 count
thread - count [2719]
thread - count [4da4]
 
count0= 19876
res [4da4]
 
count1= 19876
res [4da4]

지역변수 count
thread - count [2710]
thread - count [2710]
 
count0= 173879345
res [a5d3031]
count1= 173879345
res [a5d3031]

스레드 생성. 소멸 시간에 따라서. 주소가 다르게 나타나기도 합니다. 값도 결과도 다릅니다. (값을 많이 준경우 10000000)

thread - count [bf2a7b]
 
count0= 12571182
res [bfd22e]
thread - count [c3a7b4]
 
count1= 12822452
res [c3a7b4]

count 가 전역변수'이고. i가 지역변수'라서 그럴 줄 알았는데...

전역변수 int count
지역변수 int i
스레드A +1+1+1+1+1+1+1+1+1+1+1 같이 10000개
스레드B +1+1+1+1+1+1+1 같이 10000개

지역변수 int count
지역변수 int i
스레드A +1+1+1 10000개까지.
스레드B +1+1+1 10000개까지.

pthread.h 파일을 보시면. 좀 더 다양한 함수를 보실 수 있습니다.

확인해보니. 전역변수에서 이런값도 나오네요.
count0= 10000
count1= 9956

http://man7.org/linux/man-pages/man3/pthread_create.3.html

ptread_join
http://www.joinc.co.kr/modules/moniwiki/wiki.php/man/3/pthread_join

http://man7.org/linux/man-pages/man3/pthread_join.3.html
On success, pthread_join() returns 0; on error, it returns an error number.

usleep() 마이크로 초 동안 대기
http://forum.falinux.com/zbxe/index.php?document_srl=564661&mid=C_LIB

스레드 테스트 예제소스를 파일로 첨부해봅니다.

댓글 첨부 파일: 
첨부파일 크기
Package icon test pthread 연습.zip1.11 MB

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

익명 사용자의 이미지

여러 번 실행시켜보니

[1] 10000 / 20000

[2] 10000 + X / 20000

[3] 20000 / 10000 + X

3가지의 경우로 나눠 출력되고 있습니다.

[1]의 경우 i = 0 일 때 0, 1번 스레드가 모두 실행되 printf 함수 실행 전에 모두 끝나는 경우
[2]의 경우 0번 스레드가 먼저 끝나고, 1번 스레드는 그 다음 틱에 끝나는 경우
[3]의 경우 1번 스레드가 먼저 끝나고, 0번 스레드는 그 다음 틱에 끝나는 경우

로 나타났습니다. 각 스레드 마다 return 순서의 차이로 발생하는거 같은데
pthread_join함수가 return 실행까지는 보장이 안되는건가요???

leejk9592의 이미지

아 제가 잘못 작성했네요... 로그인을 안하고 해서 삭제가 안되서 답글로 수정합니다.

[1] 20000 / 10000

[2] 20000 / 10000 + X

[3] 10000 + X / 20000 입니다.

추가적으로 위에적은 [3]의 경우 반드시 어떤 스레드가 먼저 끝나거나 하는것 같지는 않습니다.

저는 답으로써 10000 / 20000을 예상했는데

join과 printf 사이에서도 다른 스레드가 실행되서 이런것 같다고 추론 중입니다.

그냥 mutex를 꼭 쓰자는 교훈을 얻긴 했는데
아직도 왜 저런 결과가 나오는지는 솔직히 잘 모르겠습니다.

peecky의 이미지

pthread_create() 시점에서 스레드는 실행됩니다. 거의 동시에 두 개의 스레드를 만들었고, 각각 실행이 됩니다.

pthread_join()은 앞에서 생성한 스레드의 결과같이 나올 때 까지 main 함수 스레드를 기다리게 하는 역할을 합니다. pthread_join()을 호출하는 시점에서 이미 스레드가 실행을 끝내고 종료된 상태일 수도 있습니다. 이때는 리턴 값을 바로 가져오게 되겠죠.

그리고 20000이 나온다는 것은 운이 좋은 경우입니다. 저의 경우 듀얼코어 머신에서
count0= 10060
count1= 14708
라는 결과가 나왔고, 싱글코어 머신에서는 thr_fn() 함수 안의 루프 수를 1000000로 늘리니
count0= 1641627
count1= 1706454
라는 결과가 나왔습니다. 이렇게 나오는 이유는 ++ 연산이 atomic 하지 않기 때문입니다. (메모리의 값을 레지스터로 읽기, 레지스터 값 1증가 시키기, 레지스터 값을 메모리에 쓰기 과정을 거치는데, 스레드0이 쓰기 전에 스레드1이 읽어가버리면 값 증가에 누락이 생겨버립니다.)

leejk9592의 이미지

감사합니다.
++연산이 atomic 하지 않다는 것도 잊고 있었네요 ㅠㅠ

gilgil의 이미지

결과는 환경에 따라 매우 다양하게 나타 납니다.

1. 1 CPU냐 멀티 CPU냐?
동시 실행이 가능한 thread의 수에 따라 실행 결과가 달라질 수 있습니다.

1. 각 thread의 실행이 1 time slice에 완료되느냐 아니냐?
10000이면 대개 1 time slice에 코드의 실행이 완료될 수 있는 크기인데 10000을 좀 더 큰 수로 바꾸면 잦은 thread switcing에 의해 결과가 달라질 수 있습니다.

3. ++ 연산이 atomic하느냐 아니냐?
이것은 CPU마다, 컴파일러가 생산해 내는 코드에 따라 달라 집니다.
local variable가 register로 할당이 되었다면 ++연산이 atomic해 질 수도 있습니다(load & save가 분리되지 않았다면).
또한 32bit, 64bit 자료의 경우 일반적으로 load나 save가 atomic하다고 볼 수 있지만,
micro code 레벨까지 내려 가면 atomic하지 않을 수도 있고 그 결과 torn read, torn write가 발생하기도 합니다.

4. 코드 예측이 최적화에 의해 달라 졌느냐?
상기 코드의 경우 release mode에서는 ++을 10000번 실행시키는 것이 아니라 한꺼번에 10000을 더할 수도 있습니다.

결론적으로 "상기 코드는 그 실행 결과는 예측할 수 없다"로 보는 것이 맞을 것입니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.