C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #1  http://sunyzero.egloos.com/4227785


C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #2 http://sunyzero.egloos.com/4227785

C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #3
  http://sunyzero.egloos.com/4234766
single construct로 지시된 구간은 단 한번만 실행된다. 실행되는 쓰레드는 여러 쓰레드중에 제일 먼저 진입하는 쓰레드이다.
  • single construct는 처음으로 진입한 쓰레드가 실행한다.
  • 나머지 쓰레드들은 single construct 끝에 존재하는 implicit barrier에서 대기한다.
  • single construct가 끝나고 모든 쓰레드들은 implicit barrier에서 동시에 시작한다.

그림에서 보이듯이 parallel 구간에서 쓰레드들 중에 한 개만  single construct를 실행하고 나머지는 뒤에 존재하는 implicit barrier에서 대기하는 것을 볼 수 있다. 그러면 위 소스코드를 컴파일하고 실행해보자. 실행결과는 예상대로 "1. Hello world"는 1번 출력되고, "2. Hello world"는 2번 실행된다.(테스트 호스트는 듀얼 코어이므로)
$ gcc -o omp_single -fopenmp omp_single.c
$ ./omp_single
1. Hello world 2. Hello world 2. Hello world

7. Master Construct

master construct는 single construct와 매우 비슷하다.
하지만 다른 점이 2가지 있다.

  • master construct 구간은 무조건 master thread (main thread)가 1번 실행한다.
  • master construct 구간뒤에 implicit barrier가 없다. 
    즉 모든 쓰레드는 master construct 실행되는 동안에도 계속 실행한다.
실행 결과는 위의 single construct와 같지만 위 그림에서 보듯이 약간의 차이는 있다. master construct는 implicit barrier가 없다는 점이다. 중요한 차이므로 꼭 기억해야 한다. 


8. Barrier

배리어란 동기화(synchronization)을 위해서 사용되는 기능이다.

동기화는 시간적 개념이다. 풀어서 설명하기 위해 예를 들자. 스타크래프트 배틀넷은 왠만한 사람이면 다 해봤을 것이다. 최대 8명까지 게임에 참가할 수 있는데, 어떤 유저가 매우 느린 모뎀을 쓰고 있으면 게임 중간에 타임을 세는 화면이 뜨고 기다려주는 것을 볼 수 있다. 이는 빠른 네트워크/컴퓨터를 가진 유저와 느린 네트워크/컴퓨터를 가진 유저의 게임 속도를 맞추기 위해서 배리어가 작동한 것이다. 따라서 결과적으로 배리어는 느린 사람에 맞춰서 앞서 가는 사람이 대기하도록 하는 기능이다.

그러면 프로그래밍에서는 배리어를 어떻게 사용해야 하는가? 작업이 병렬적으로 이뤄진다고 하더라도 전처리, 후처리 작업들이 나눠져 있을 경우에는 전처리 작업들을 병렬처리했을때 어떤 특정 쓰레드가 빨리 처리했다고 후처리 작업을 먼저 출발하면 데이터가 꼬이거나 로직이 망가질 수 있다. 이럴 경우 중간중간에 적절한 배리어를 넣어주면 깔끔하게 해결된다.(하지만 역으로 배리어가 많으면 그 만큼 대기도 많아질 수 있다.)

8.a Implicit barrier

앞에서 implicit barrier(암묵적 배리어)에 대해서 이야기를 많이 했다. OpenMP는 각 작업의 동기화에 대한 편의성을 제공하기 위해서 implicit barrier를 잘 제공한다. 어떤 construct 에 대해서 implicit barrier가 제공되는지 정리하고 넘어가자.
  • #pragma omp parallel
  • #pragma omp for
  • #pragma omp sections
  • #pragma omp single
위의 4가지의 경우는 블록 끝에 자동적으로 implicit barrier가 들어간다. 하지만 위의 4가지 construct 의 끝에 nowait clause를 지정하면 implicit barrier가 제거되고 대기하지 않고 이후 코드가 즉시 실행된다.

위의 예제에서는 single construct에 nowait를 적용하여 implicit barrier를 제거하는 것을 볼 수 있다. 
(그림 아래에 있는 implicit barrier는 parallel construct에 있는 barrier다.)

8.b Explicit barrier

이번에는 사용자가 직접 지정할 수 있는 explicit barrier 기능에 대해서 보겠습니다.
  • #pragma omp barrier 구문을 지정하면 해당 부분에서 모든 쓰레드가 도착할 때까지 대기하게 된다.
char * get_time0(char *buf, size_t sz_buf);
int main() {
int t_sleep; char buf[16];
#pragma omp parallel private(t_sleep, buf)
{
#pragma omp single nowait
sleep(1);
printf("[%s] phase1:sleep %ld sec.\n", get_time0(buf, sizeof(buf)), t_sleep = times(NULL)%8);
sleep(t_sleep);
#pragma omp barrier
/* explicit barrier */
printf("[%s] phase2. Hello world\n", get_time0(buf, sizeof(buf)));
}
return 0;
}
char * get_time0(char *buf, size_t sz_buf)
{
time_t t0; struct tm tm_now;
if (buf == NULL) return NULL;
if (time(&t0) == ((time_t)-1)) return NULL;
localtime_r(&t0, &tm_now);
if (strftime(buf, sz_buf, "%H:%M:%S", &tm_now) == 0) return NULL;
return buf;
}
이제 실행해보면 배리어 효과때문에 마지막 실행한 19:12:28에서 5초뒤에 phase2가 실행되는 것을 볼 수 있다.
$ ./omp_barrier
[19:12:27] phase1:sleep 1 sec.
[19:12:28] phase1:sleep 5 sec.
[19:12:33] phase2. Hello world
[19:12:33] phase2. Hello world
_M#]

C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #
4 http://sunyzero.egloos.com/4258873

"for loop실행 중 Timer Event가 제대로 작동하나요?"

for loop가 약 30분 정도 돌아가야 되는데(기계제어 프로그램이기 때문에 for loop내에서 여러조건이 만족되어야 for loop내에서 다음 단계(step)로 넘어감) 이 과정중에 Timer Event를 사용할 필요가 있습니다. 즉 For Loop내에서 단계(step)가 진행될 때마다 global Variable이 변하게 되는데 이 때 이벤트를 걸어 다른 작업(Com2 Port를 통해 순간적으로 수신/전송을 해야 됨)을 해야 되기 때문이거든요. 그런데 for loop내에서 이 다른작업이라는 놈을 넣어니 최초 한번만 실행 되고 그 이후에는 반응이 없더라구요. 그래서 Timer event를 이용하여 일정간격으로 이 다른작업이라는 놈을 실행되도록 했더니 되긴 되는데 제대로 되지 않더군요. 즉 Global Variable은 계속 바뀌는데 Timer Event 함수내에서 이 Global 변수를 추적해 보니까 Timer함수내로 이 변수가 제대로 넘어오지 않더군요. (이하생략)


>> 답변 : 지나가는사람 님 ( bcbdn C++Builder Q&A 14815번 )

Timer의 경우는 그런상태가 벌어지는 경우가 많습니다.
저도 비슷한 문제로 여러가지로 테스트를 해보았는데...
윈98 계열의 경우는 그러한 상태가 일어나는 경우가 상당히 많고.. NT 계열은 그나마 좀 낮습니다.
(98은 마우스로 캡션바를 드래그 & 드롭하는 경우에도 Timer는 멈춥니다.)
그렇지만.. NT라도 제대로 제어를 하기 위해서는 스레드는 필수입니다.

>> 추가 : for loop나 while문을 계속 돌리고 싶은 경우 빠져나오는 조건이 명확하지 않다면 Thread를 쓰시는 편이 시스템 전체가 다운되는 현상을 방지할 수 있습니다.
Thread는 File > New > Thread Object 이러한 과정을 생성하면 되구요. 예를 들면 아래와 같이 생성할 수도 있습니다.

>> 참고

@ Execute
쓰레드 실행시에 실행 코드를 가지기 위한 순수 가상 메쏘드를 제공한다
(원형) virtual void __fastcall Execute(void) = 0;
(상세) 쓰레드 실행시 실행되어질 코드의 포함하고 실행하기위해 쓰인다. 이곳에서 쓰레드를 빠져나갈 조건을 체크한다.
CreateSuspended = false 라면 Create, CreateSuspended = true 라면 쓰레드를 생성 후에
Resume이 호출되었을 때 쓰레드는 실행한다.

(주의) 다른 오브젝트의 properties와 methods에서 직접적으로 쓰레드의 Execute를 사용하지 말라. 대신, procedure를 나눠서 Synchronize 메쏘드의 인자로써 procedure를 사용하라.


@ Synchronize
주요 VCL 쓰레드 호출 Executes method

(원형) typedef void __fastcall (__closure *TThreadMethod)(void);
       void __fastcall Synchronize(TThreadMethod &Method);

(상 세) Synchronize는 주 VCL 쓰레드를 사용하기위해 실행되어지는 메쏘드에만 호출되어야하기 때문에, 멀티-쓰레드에는 사용되지 말아야 한다. 쓰레드 호출이 안전할지 확실하지 않다면, 주 VCL 쓰레드를 거쳐서 Synchronize 메쏘드를 호출해라.
Execution 쓰레드는 주 VCL 쓰레드안에서 메쏘드가 실행될 동안에는 정지되어 있다.
(주의) critical section이나 multi-read exclusive-write synchronizer에서 사용되는 불안정한 메쏘드를 보호할 수 있다.

@ Priority
쓰레드 프로세스에서 상대적인 스케줄링 우선순위
(원형) enum TThreadPriority {tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical};
       __property TThreadPriority Priority = {read=GetPriority, write=SetPriority,nodefault};

(상세) Priority는 쓰레드 스케줄링시에 사용되는 우선순위이다.
TThreadPriority형은 아래에 정의된 표에 따라 TThread 콤포넌트의 Priority property 값을 정의한다. Windows 우선순위 스케일에 기준으로 각 쓰레드의 CPU cycle을 스케쥴한다.

값                       내용

tpIdle                  시스템이 idle 상태일때만 실행된다. tpIdle의 우선순위를 가진  쓰레드의 실행때문에 다른 쓰레드 프로세스를 interrupt 하지는 않을 것이다.
tpLowest                  normal보다 2 point 낮은 우선순위를 가진다.
tpLower                  normal보다 1 point 낮은 우선순위를 가진다.
tpNormal                  normal 우선순위이다.
tpHigher                  normal보다 1 point 높은 우선순위를 가진다.
tpHighest                  normal보다 2 point 높은 우선순위를 가진다.
tpTimeCritical         최고 높은 우선순위를 가진다.

주 의 : CPU 점유율이 높은 operation의 우선순위를 높이는 것은 동일 Application내에 타 쓰레드 가 동작하지 못하게 할 수도 있다. External event를 기다리는데 대부분의 시간을 사용하는 쓰레드의 우선순위를 높여라.


@ WaitFor

쓰레드를 종료되기를 기다리고,  종료시 ReturnValue property를 return한다.

(원형) int __fastcall WaitFor(void);

(상 세)  WaitFor를 호출하면 쓰레드 종료 시 ReturnValue를 얻을 수 있다. WaitFor는  Execute 메쏘드가 끝나거나 Terminated property = true 가 되어 빠져나와서 쓰레드가 종료될때까지 return되지 않는다.

--------------------------------------------------------------------------------------------------

이번에 BDS 2006으로 바뀌면서 몇가지 바뀐것이 있는데요,
그중 하나가 Synchronize의 사용법 입니다. 저는 이걸 몰라서 몇일동안 고생해서
외국의 포럼에서 찾은 내용 팁란에 올립니다.

기존에는
void __fastcall CTEST::Test()

라는 메소드를 동기화 시키려면
Synchronize(Test);
라고만 쓰면 됬는데 새롭게 바뀐 문법에서는

Synchronize(&Test);

이렇게 쓰셔야 에러 안납니다.

------------ 볼랜드포럼에서 발췌 ---------------------------------------

+ Recent posts