C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[72676] Re: 바이너리 세마포어, 카운팅 세마포어 ...
빌더(TWx) [builder] 5282 읽음    2015-05-23 21:07
김상면 님이 쓰신 글 :
: 유닉스에서 세마포를 사용할때
: 왜 바이너리세마포만 설명을 하죠....
:
: 일반 세마포는 어떻게 만드나요?
: 그러니까 동시 접근 가능한 프로세스를 n개로 만드는 방법이 궁금합니다.
:
: 그럼



답변...


바이너리 세마포어로도 충분히 설명이 되니까 그런 거죠.
바이너리 세마포어는 오너쉽 개념을 갖고있는 뮤텍스하고 기본 개념은 똑같습니다.


그리고...

바이너리 세마포어라고 해서 동시 접근 가능한 프로세스(쓰레드)가 n개가 될수 없는 게 아닙니다.
바이너리 세마포어의 개념은 2개 이상의 쓰레드들이 경합을 다투는 리소스가 하나임을 의미하고
하나의 리소스를 두고 허용이냐 아니냐의 두가지의 경합상태를 바이너리라는 용어를 빌려서 표현한 거죠.


바이너리 세마포어 sem_init(,,1) 과 pthread_mutex_init(&mutex..)는 둘다 개념적으로는 똑같습니다.

System V API는 구조가 난잡해서 버그를 양산할 가능성도 많고, 대부분 POSIX 인터페이스를 선호하게 되죠.



이해하기 쉽게 예를 하나 들어 봅시다...


좌변기가(리소스) 4개 설치되어있는 공중화장실이 있다고 해봅시다.


sem_init(,,,4) 로 세마포어 카운트를 4로 지정하면(카운팅 세마포어)...


void 화장실 사용()
{
   sem_wait(&sema);
   ...
}

한명이 들어갈 때 마다.. 세마포어 카운트가 1씩 감소 되고... 0이 되면 블러킹 상태가 됩니다.
4명이 다 사용중이면 볼일을 못 보고 기다려야 하죠.그러나 여기에 오너쉽 개념은 없습니다. (세마포어)


좌변기에 앉은 사람은 문을 걸어 잠그고 볼일을 보기 시작 합니다.
좌변기 하나에 여러명이 같이 앉아서 똥을 쌀수는 없으니까... 문을 잠그고 볼일을 보는 동안은 이 사람은(쓰레드)
좌변기를 독점적으로 사용하기 시작합니다. (오너쉽 관계)


좌변기(리소스)를 사용하고 문을 열고 나오지 않는 이상 (오너쉽을 갖고있는 이사람만 릴리즈도 가능)
이 사람은(쓰레드) 좌변기(리소스)의 오너쉽을 갖게 되므로..
여기서 바이너리 세마포어와 기본 구조는 같지만 쓰레드 오너쉽 개념을 갖고있는 mutex를 써먹는 게 안성 맞춤입니다.




void 화장실 사용()
{
   sem_wait(&sema);
   ...
// 좌변기를 획득하는 구체적인 코드는 생략
   pthread_mutex_lock(&mutex);  
   좌변기에 앉아서 볼일 보기 시작
   ....  
   pthread_mutex_unlock( &mutex);      
   seme_post(&sema);
}


pthread_create로 화장실을 사용할 쓰레드(사람들)를 10개를 생성하든.. 20개를 생성하든 하면 되는 거고...


세마포어, 뮤텍스, 이벤트 등을 API에 의존하지 말고.. 직접 구현해 보면 여러가지 동기화 방법들의 차이점을
극명하게 구분하고 이해할 수 있을 텐데요...


쓰레드 동기화를 배울 때... 기초 과정으로 많이 다루는.. 생산자와 소비자 관계를 세마포어와 뮤텍스로 다뤄 봅시다.


#define BUFFER_SIZE 4096

static STREAM_DATA streamBuffer[BUFFER_SIZE];

static pthread_mutex_t mutex;
static sem_t sema_empty;
static sem_t sema_full;

static int insertIndex = 0;
static int removeIndex = 0;

int insertStreamData( STREAM_DATA data )
{
    sem_wait( &sema_empty ); 
    
    pthread_mutex_lock( &mutex );

    streamBuffer[insertIndex++] = data;
    insertIndex = insertIndex % BUFFER_SIZE;

    pthread_mutex_unlock( &mutex );
    sem_post( &sema_full );        

    return 0;
}

int removeStreamData( STREAM_DATA *data )
{
    sem_wait( &sema_full );     

    pthread_mutex_lock( &mutex );
    *data = streamBuffer[removeIndex];
    streamBuffer[removeIndex++] = -1;
    removeIndex = removeIndex % BUFFER_SIZE;

    pthread_mutex_unlock( &mutex ); 
    sem_post( &sema_empty );        

    return 0;
}

#define SIMULATION_DELAY  20000

void *decodeWorkerThread( void *param )
{
    STREAM_DATA data;

    int threadIndex = (int)param;

    while ( true )
    {
        // simulate decode works
        usleep( rand() % SIMULATION_DELAY );
        data = rand() + ( id % BUFFER_SIZE );

        insertStreamData( data );
        printf( "decode Thread # %d produced %d \n", threadIndex, data );

    }
    printf( "\n decode Thread # %d finished and exiting. \n", threadIndex );
    pthread_exit( 0 );
}

void *playbackWorkerThread( void *param )
{
    STREAM_DATA data;

    int threadIndex = (int)param;

    while (true)
    {
        removeStreamData( &data );

        // simulate playback works
        usleep( rand() % SIMULATION_DELAY );

        printf( "playback Thread # %d consumed %d \n", threadIndex, data );
    }
    printf( "\n playback Thread # %d finished and exiting. \n", threadIndex );
    pthread_exit( 0 );
}

const int N_PRODUCER_THREADS    3
const int N_CONSUMER_THREADS    3

int main( int argc, char *argv[] )
{
    pthread_mutex_init( &mutex, NULL );
    sem_init( &sema_empty, 0, BUFFER_SIZE);
    sem_init( &sema_full, 0, 0 );
    srand( time( 0 ) );

    pthread_t* decodeThreadID = new pthread_t[N_PRODUCER_THREADS];

    for( int i = 0; i < N_PRODUCER_THREADS; i++ )
    {
        pthread_attr_t attr;
        pthread_attr_init( &attr );
        pthread_create( &decodeThreadID[i], &attr, decodeWorkerThread, (void*)i );
    }

    pthread_t* playbackThreadID = new pthread_t[N_CONSUMER_THREADS];
    for( int j = 0; j < N_CONSUMER_THREADS; j++ )
    {
        pthread_attr_t attr;
        pthread_attr_init( &attr );
        pthread_create( &playbackThreadID[j], &attr, playbackWorkerThread, (void*)j );
    }

    for( int i = 0; i < N_PRODUCER_THREADS; i++ )
    {
        pthread_join( decodeThreadID[i], NULL ); 
    }

    for( int j = 0; j < N_CONSUMER_THREADS; j++ ) 
    {
        pthread_join( playbackThreadID[j], NULL );
    }

    printf( "main thread has terminated.\n" );
    return 0;
}





코드가 간단해서 이해하는데 어려움이 없을 겁니다. 만약 이해하는데 어려움을 겪는다면 동기화 개념이 아직...


+ -

관련 글 리스트
72674 유닉스에서 김상면 3827 2015/05/22
72676     Re: 바이너리 세마포어, 카운팅 세마포어 ... 빌더(TWx) 5282 2015/05/23
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.