안녕하세요, 시스템 프로그래밍 과목 시험문제인데 전문가 분들의 도움이 필요합니다...
글쓴이: leejk9592 / 작성시간: 일, 2015/12/13 - 5:45오후
#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
이렇게 나오는데 왜 이런지 이해가 잘 가지 않습니다....
리눅스 전문가들이 많이 계시다고 해서 올려봅니다
작은 지식을 나눠 주신다면 정말 감사드리겠습니다.
Forums:
for(i=0; i<10000; i++) 는 10000씩 +해줍니다.
- 전역변수 일 경우. 참조한 res 의 포인터 주소' 가 count 의 주소와 같습니다.
- 스레드 마다. count = 0; 으로 초기화 하시면. 값이 또 달라집니다.
- int i=0; 은 지역변수 입니다.
- 변수 이름과 스레드 이름을 별도로 생성해서 사용하시면. 중복 문제는 적어집니다.
스레드 생성. 소멸 시간에 따라서. 주소가 다르게 나타나기도 합니다. 값도 결과도 다릅니다. (값을 많이 준경우 10000000)
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
스레드 테스트 예제소스를 파일로 첨부해봅니다.
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 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 실행까지는 보장이 안되는건가요???
아 제가 잘못 작성했네요... 로그인을 안하고 해서
아 제가 잘못 작성했네요... 로그인을 안하고 해서 삭제가 안되서 답글로 수정합니다.
[1] 20000 / 10000
[2] 20000 / 10000 + X
[3] 10000 + X / 20000 입니다.
추가적으로 위에적은 [3]의 경우 반드시 어떤 스레드가 먼저 끝나거나 하는것 같지는 않습니다.
저는 답으로써 10000 / 20000을 예상했는데
join과 printf 사이에서도 다른 스레드가 실행되서 이런것 같다고 추론 중입니다.
그냥 mutex를 꼭 쓰자는 교훈을 얻긴 했는데
아직도 왜 저런 결과가 나오는지는 솔직히 잘 모르겠습니다.
pthread_create() 시점에서 스레드는
pthread_create() 시점에서 스레드는 실행됩니다. 거의 동시에 두 개의 스레드를 만들었고, 각각 실행이 됩니다.
pthread_join()은 앞에서 생성한 스레드의 결과같이 나올 때 까지 main 함수 스레드를 기다리게 하는 역할을 합니다. pthread_join()을 호출하는 시점에서 이미 스레드가 실행을 끝내고 종료된 상태일 수도 있습니다. 이때는 리턴 값을 바로 가져오게 되겠죠.
그리고 20000이 나온다는 것은 운이 좋은 경우입니다. 저의 경우 듀얼코어 머신에서
count0= 10060
count1= 14708
라는 결과가 나왔고, 싱글코어 머신에서는 thr_fn() 함수 안의 루프 수를 1000000로 늘리니
count0= 1641627
count1= 1706454
라는 결과가 나왔습니다. 이렇게 나오는 이유는 ++ 연산이 atomic 하지 않기 때문입니다. (메모리의 값을 레지스터로 읽기, 레지스터 값 1증가 시키기, 레지스터 값을 메모리에 쓰기 과정을 거치는데, 스레드0이 쓰기 전에 스레드1이 읽어가버리면 값 증가에 누락이 생겨버립니다.)
싱글코어의 경우에 확연히 드러나는군요
감사합니다.
++연산이 atomic 하지 않다는 것도 잊고 있었네요 ㅠㅠ
gilgil.net
결과는 환경에 따라 매우 다양하게 나타 납니다.
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을 더할 수도 있습니다.
결론적으로 "상기 코드는 그 실행 결과는 예측할 수 없다"로 보는 것이 맞을 것입니다.
www.gilgil.net
댓글 달기