×
Crocus
공부한 내용을 정리하는 블로그로 시작한
Crocus는 2014년 1월 14일 부터 시작하여
현재 월 6만명, 총 2,045,673명의 방문자 수를 기록하고 있습니다.
Donation
이제 많은 사용자들이 이용하는 만큼
더 다양한 서비스 개발/제공을 위해 후원금을 모금하고자 합니다.
후원을 해주시는 분들은 Donators 명단에 성명, 후원금을 기입해드리며
Crocus 블로그가 아닌 다른 곳에 정리해둔 저만의 내용을 공유해 드리고자 합니다.
Account
예금주 : 고관우
신한은행 : 110-334-866541
카카오뱅크 : 3333-01-7888060

👉 후원 페이지 바로가기 Donators
익명 : 5000원(Crocus응원합니다.)
busyhuman: 5000원(유용한 지식 감사합니다.)
익명 : 5000원(알고리즘 학습러)
반응형


Semaphore(세마포어)


운영 체계 또는 프로그램 작성 내에서 공유 자원에 대한 접속을 제어하기 위해 사용되는 신호. 


병행 내지 병렬로 동작되는 둘 이상의 프로세서 사이에서 마이크로프로세서 시간이나 

입출력 접속구와 같은 공유 자원을 동시에 사용할 수 없기 때문에, 

한 프로세서가 사용하고 있는 동안에 세마포어를 세워서 다른 프로세서를 대기시키고 

사용이 끝나면 해제시키는 방법으로 사용한다.


(출처 : http://terms.naver.com/entry.nhn?docId=852341&cid=42346&categoryId=42346)


예를 들면 다음과 같다. 


세마포어는 지하철 화장실처럼 화장실 칸의 개수가 여러개 유한하게 있는 곳의 열쇠의 갯수이다.


뮤텍스는 1개의 화장실에 대해서 다룬다면, 세마포어는 여러개의 화장실로 다루게 되고


화장실을 쓰기위해 열쇠를 하나 가져가면 P, 열쇠를 반납하면 V라고 한다.


만약 열쇠를 계속 가져가서(P) 열쇠가 0개가 되면, 다음 사람은 줄을 서서 기다려야한다.


화장실을 다 쓴다음 열쇠를 반납하게되면(V) 다음 사람이 들어갈 수 있다.


세마포어의 장단점


 - 장점

   많은 스레드들은 크리티컬 섹션을 허락받아야 한다.

   즉, 크리티컬 섹션에서 충돌이 나지 않는다.


 - 단점

   많은 스레드들은 block을 당한다.

   즉, CPU가 가만히 waiting하는 시간 낭비가 생길 수 있다.



예제 코드


위의 예시처럼 화장실을 이용한 코드를 작성해 보았다.


sem_t semaphore; 


세마포어 생성을 의미한다.


sem_init(sem_t *sem, int pshared, unsigned int value);


첫번째 인자 :: 세마포어 객체를 초기화 할 세마포어를 받는다. (위의 sem_t semaphore가 여기 해당된다.)

두번째 인자 :: 여기에 0을 주지 않을경우 sem_init는 항상 ENOSYS 에러코드를 반환한다.(0을 쓰도록 한다.)

세번째 인자 :: 세마포어를 몇으로 초기화 할지 의미한다.(화장실 열쇠를 몇개로 시작할지 의미)


sem_wait(sem_t *sem);


세마포어의 P역할을 한다.

즉, 세마포어를 하나 감소시키는 역할을 하고 세마포어가 0일 경우에는 1이상이 될 때까지 스레드는 대기 상태에 있는다.

0이 아닐 경우에는 대기상태에서 빠져나와 세마포어를 또 1 감소시킨다.


sem_post(sem_t *sem);


세마포어의 V역할을 한다.

세마포어 값을 1 증가 시킨다.


sem_destroy(sem_t *sem);


세마포어와 관련된 리소스들을 소멸시킨다. (객체 소멸)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Using Semaphore
 
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
 
sem_t semaphore;
 
int num = 2;
 
void *threadF1(void *arg)
{
    int i;
    if(num != 0)
    {
        num--;
        sem_wait(&semaphore);
        printf("스레드 1이 화장실을 이용하기 시작합니다.\n");
 
        // 화장실 이용하는 시간이라 가정
        for(i = ; i < 1000000 ; i++){}
 
        printf("스레드 1이 화장실 이용을 마쳤습니다.\n");
        num++;
        sem_post(&semaphore);
    }
    
    else if(num == 0)
    {
        printf("화장실이 꽉 차 스레드 1이 기다리고 있습니다.\n");
        
        while(num == 0){}
        
        printf("스레드 1이 화장실 이용을 하고 마쳤습니다.\n");
        num++;
        sem_post(&semaphore);
    }
    
    return NULL;
}
 
void *threadF2(void *arg)
{
    int i;
    if(num != 0)
    {
        num--;
        sem_wait(&semaphore);
        printf("스레드 2가 화장실을 이용하기 시작합니다.\n");
 
        // 화장실 이용하는 시간이라 가정
        for(i = ; i < 1000000 ; i++){}
 
        printf("스레드 2가 화장실 이용을 마쳤습니다.\n");
        num++;
        sem_post(&semaphore);
    }
    
    else if(num == 0)
    {
        printf("화장실이 꽉 차 스레드 2가 기다리고 있습니다.\n");
        while(num == 0){}
 
        printf("스레드 2가 화장실 이용을 하고 마쳤습니다.\n");
        num++;
        sem_post(&semaphore);
    }
        
    
    return NULL;
}
 
void *threadF3(void *arg)
{
    int i;
    if(num != 0)
    {
        num--;
        sem_wait(&semaphore);
        printf("스레드 3이 화장실을 이용하기 시작합니다.\n");
 
        // 화장실 이용하는 시간이라 가정
        for(i = ; i < 1000000 ; i++){}
 
        printf("스레드 3이 화장실 이용을 마쳤습니다.\n");
        num++;
        sem_post(&semaphore);
    }
    
    else if(num == 0)
    {
        printf("화장실이 꽉 차 스레드 3이 기다리고 있습니다.\n");
        while(num == 0){}
 
        printf("스레드 3이 화장실 이용을 하고 마쳤습니다.\n");
        sem_post(&semaphore);
        num++;
    }
        
    
    return NULL;
}
 
int main()
{
 
    pthread_t thread1, thread2, thread3;
 
 
    sem_init(&semaphore, 02); // return :: 0 -> success , others -> fail
 
 
    printf("Semaphore test Start!\n");
    // 스레드 생성
    pthread_create(&thread1, NULL, threadF1, NULL);
    pthread_create(&thread2, NULL, threadF2, NULL);
    pthread_create(&thread3, NULL, threadF3, NULL);
 
    // 스레드 조인
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    pthread_join(thread3, NULL);
 
    printf("모든 스레드가 화장실 이용을 끝냈습니다.\n");
 
    // 세마포어 객체 소멸
    sem_destroy(&semaphore);
 
    return 0;
}
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus






< 여기서 잠깐 >


4번째 예시를 보면 다음과 같이 형성되어있다.



자세히 보면 말이조금 안맞다.


즉, 1 3이 화장실을 들어갔다가 끝냈는데 왜 갑자기 2가 화장실이 꽉 차있다고하는가?



이것에 대한 해답은 다음과 같다.


... 생략 ...


printf("스레드 3이 화장실 이용을 마쳤습니다.\n");

num++;

sem_post(&semaphore);

}


이 부분을 보면 printf를 하고 난 즉시, 컨텍스트 스위칭을 당했음을 알 수 있다.


그래서 num 및 V(sem_post)가 일어나지 않아 꽉 찼다고 printf된것이다.


세마포어 예시를 제작하는데 이러한 사소한 부분까지 다 고려해 주어야 완벽한 세마포어가 될것이다.


































반응형
  1. ㅇㅇ 2019.11.05 16:59

    뭔가 소스코드가 이상한데..

    • 가누 2019.11.05 21:19 신고

      어떤부분을 말씀하시는지 구체적으로 적어주실 수 있겠어요?