뮤텍스 예제 사용해보자!

이호용·2021년 7월 12일
0

Philosophers

목록 보기
3/5

1. 뮤텍스 예제

참고 예제 너무 감사합니다

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

int ncount;    // 쓰레드간 공유되는 자원
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화

void* do_loop(void *data)
{
    int i;
    for (i = 0; i < 10; i++)
    {
        pthread_mutex_lock(&mutex); // 잠금을 생성한다.
        printf("loop1 : %d\n", ncount);
        ncount ++;
        if(i == 10) break;
        pthread_mutex_unlock(&mutex); // 잠금을 해제한다.
        sleep(1);
    }
	return 0;
}

void* do_loop2(void *data)
{
    int i;

    // 잠금을 얻으려고 하지만 do_loop 에서 이미 잠금을
    // 얻었음으로 잠금이 해제될때까지 기다린다.
    for (i = 0; i < 10; i++)
    {
        pthread_mutex_lock(&mutex); // 잠금을 생성한다.
        printf("loop2 : %d\n", ncount);
        ncount ++;
        pthread_mutex_unlock(&mutex); // 잠금을 해제한다.
        sleep(2);
    }
	return 0;
}

int main()
{
    int       thr_id;
    pthread_t p_thread[2];
    int status;
    int a = 1;

    ncount = 0;
    thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a);
    sleep(1);
    thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a);

    pthread_join(p_thread[0], (void *) &status);
    pthread_join(p_thread[1], (void *) &status);
	// 부모프레스가 얘들 끝날ㄱ떄까지 join에서 기다리는듯.
    status = pthread_mutex_destroy(&mutex); //뮤텍스 파ㄷ괴!!!
    printf("code  =  %d", status);
    printf("programing is end");
    return 0;
}

로직을 쭉 보니 다른건 다 이해가 되었는데, 이해하기 힘든 부분이, pthread_create를 하게 되면? 어떻게 되는걸까?
예전에 fork로 프로세스를 생성했을떈, 같은 코드에서 자식프로세스와 부모프로세스로 나뉘어 한줄씩 같이 내려갔다.
그런데 스레드는 로직을 보니, 메인문에서 같이 내려가는게 아니라 스레드를 생성할떄 같이 넣어준, do_loop함수를 타는거 같았다.
스레드라는게, 프로세스에서 실행하는 흐름. 이라고 했던거 같은데 흠.. 스레드를 만들어줄떄 넣어준 함수만 동작하는 걸까???

아래 코드에서 확인해보자.

2. 뮤텍스 예제2

너무너무 감사합니다. 여기 링크의 코드를 참고햇어요!

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
 
// 쓰레드 함수
void *t_function(void *data)
{
    pid_t pid;            // process id
    pthread_t tid;        // thread id
 
    pid = getpid();
    tid = pthread_self();
 
    char* thread_name = (char*)data;
    int i = 0;
 
    while (i<3)   // 0,1,2 까지만 loop 돌립니다.
    {
        // 넘겨받은 쓰레드 이름과 
        // 현재 process id 와 thread id 를 함께 출력
        printf("[%s] pid:%u, tid:%x --- %d\n", 
            thread_name, (unsigned int)pid, (unsigned int)tid, i);
        i++;
        sleep(1);  // 1초간 대기
    }
}
 
int main()
{
    pthread_t p_thread[2];
    int thr_id;
    int status;
    char p1[] = "thread_1";   // 1번 쓰레드 이름
    char p2[] = "thread_2";   // 2번 쓰레드 이름
    char pM[] = "thread_m";   // 메인 쓰레드 이름
 
 
    sleep(1);  // 2초 대기후 쓰레드 생성
 
    // ① 1번 쓰레드 생성
    // 쓰레드 생성시 함수는 t_function
    // t_function 의 매개변수로 p1 을 넘긴다.  
    thr_id = pthread_create(&p_thread[0], NULL, t_function, (void *)p1);
 
    // pthread_create() 으로 성공적으로 쓰레드가 생성되면 0 이 리턴됩니다
    if (thr_id < 0)
    {
        perror("thread create error : ");
        exit(0);
    }
 
    // ② 2번 쓰레드 생성
    thr_id = pthread_create(&p_thread[1], NULL, t_function, (void *)p2);
    if (thr_id < 0)
    {
        perror("thread create error : ");
        exit(0);
    }
 
    // ③ main() 함수에서도 쓰레드에서 돌아가고 있는 동일한 함수 실행
    t_function((void *)pM);
 
    // 쓰레드 종료를 기다린다. 
    pthread_join(p_thread[0], (void **)&status);
    pthread_join(p_thread[1], (void **)&status);
 
    printf("언제 종료 될까요?\n");
 
    return 0;

위의 코드를 보면, 메인문에 "언제 종료 될까요?" 라는 문구가 있다. 확인을 해보니 한번찍힌다. 아무래도, 프로세스랑 다르게 스레드는 생성할때 만들어준 함수만 타게 되는거 같다.

자 이제 철학자 문제에 대한 코드를 작성해보자.

3. 뮤텍스 예제

철학자들이 죽지않고 적가락을 들려면 3가지 방법이 있다.
1. 젓가락수가 철학자보다 많은것
2. 누군가 한명이 반대 젓가락을 먼저 드는것
3. 철학자가 2개의 젓가락을 모두 집을 수 있을떄만 집게 하는것!

이번 예제에서는 2번인 누군가 한명이 반대 젓가락을 먼저드는 예제를 살펴본다.

twinw의 흰고래꿈 블로그를 참고 했다. 너무 감사합니다.

해결 방안인 누군가 한명이 반대 젓가락을 먼저 집게 하는 방식을 조금 변경하여 짝수 철학자와 홀수 철학자로 나누어 반대 방향 젓가락을 먼저 집게 하도록 구현하였습니다. C언어로 구현하였으며 mutex와 condition을 이용하여 구현하였습니다.

포크(젓가락)을 들고 내려 놓을 때 mutex로 접근을 제한했고 permits 변수를 사용하여 포크의 사용여부를 나타 내었습니다.

한번 뜯어서 살펴보자..

이해하는데 참고한 자료 :
sys/types.h - data types
pthread.h
뮤텍스사용법 - 좋아용~

이거 읽고 잇엇음.

감사합니다. 흰고래님.

#include <stdio.h>
#include <pthread.h>

#define NUM_THREADS 5

pthread_mutex_t mutexes[NUM_THREADS]; //mutex를 사용한다고 선언, pthread_mutex_t 자료형임.
pthread_cond_t conditionVars[NUM_THREADS]; // 데이터형은 pthread_cond_t 이다. 뮤텍스 정적 초기화

int permits[NUM_THREADS]; //포크 사용여
pthread_t tids[NUM_THREADS]; // 스레드 담을 그릇

void pickup_forks(int philosopher_number) {
    pthread_mutex_lock(&mutexes[philosopher_number%NUM_THREADS]);
    while (permits[philosopher_number%NUM_THREADS] == 0) {//젓가락 쓸수 있나 확인하고
        pthread_cond_wait(&conditionVars[philosopher_number%NUM_THREADS], &mutexes[philosopher_number%NUM_THREADS]);//못쓰면 wait를 걸어 mutexes락을 해제합니다.이 스레드는 잠시 뒤로 미뤄두고 conditionVars를 기다립니다.
    }
    permits[philosopher_number%NUM_THREADS] = 0;
    pthread_mutex_unlock(&mutexes[philosopher_number%NUM_THREADS]);
}

void return_forks(int philosopher_number) {
    pthread_mutex_lock(&mutexes[philosopher_number%NUM_THREADS]);
    permits[philosopher_number%NUM_THREADS] = 1;
    pthread_cond_signal(&conditionVars[philosopher_number%NUM_THREADS]);
    pthread_mutex_unlock(&mutexes[philosopher_number%NUM_THREADS]);

}

void* Philosopher(void * arg) {
    int philosopher_number;
    philosopher_number = (int)arg;

    // pickup left fork
    pickup_forks(philosopher_number);
    printf("philosopher(%d) picks up the fork(%d).\n", philosopher_number, philosopher_number);

    // pickup left fork
    pickup_forks(philosopher_number);
    printf("philosopher(%d) picks up the fork(%d).\n", philosopher_number, philosopher_number);

    // pickup right fork
    pickup_forks(philosopher_number+1);
    printf("philosopher(%d) picks up the fork(%d).\n", philosopher_number, (philosopher_number + 1) % NUM_THREADS);

    printf("philosopher(%d) starts eating \n", philosopher_number);
    sleep(2);
    printf("philosopher(%d) finishes eating \n", philosopher_number);

    // putdown right fork
    return_forks(philosopher_number + 1);
    printf("philosopher(%d) put down the fork(%d).\n", philosopher_number, (philosopher_number + 1) % NUM_THREADS);

    // putdown left fork
    return_forks(philosopher_number);
    printf("philosopher(%d) put down the fork(%d).\n", philosopher_number, philosopher_number);

    return NULL;
}
 void * OddPhilosopher(void * arg) {
    int philosopher_number;
    philosopher_number = (int)arg; //필로소퍼 번호

    // pickup right fork
    pickup_forks(philosopher_number + 1);
    printf("philosopher(%d) picks up the fork(%d).\n", philosopher_number, (philosopher_number + 1) % NUM_THREADS);

    // pickup left fork
    pickup_forks(philosopher_number);
    printf("philosopher(%d) picks up the fork(%d).\n", philosopher_number, philosopher_number);

    printf("philosopher(%d) starts eating \n", philosopher_number);
    sleep(2);
    printf("philosopher(%d) finishes eating \n", philosopher_number);

    // putdown left fork
    return_forks(philosopher_number);
    printf("philosopher(%d) puts down the fork(%d).\n", philosopher_number, philosopher_number);

    // putdown right fork
    return_forks(philosopher_number + 1);
    printf("philosopher(%d) puts down the fork(%d).\n", philosopher_number, (philosopher_number + 1) % NUM_THREADS);

    return NULL;
}

int main() {
    int i;

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_mutex_init(&mutexes[i], NULL); //이걸로 뮤텍스 초기화!! 기본으로 초기화 한다.
        pthread_cond_init(&conditionVars[i], NULL);// cond는 조건 변수인거같다. 조건 변수르 초기화한다는거 같은데, 조건변수??? 그게 뭐지??
        permits[i] = 1; // 포크 아무도 안쓰니까 일딴 1로 모두 사용가능하게 초기화
    }

	// 모두 초기화 했다. 이제 스레드를 생성해 보자.
    for (i = 0; i < NUM_THREADS; i++) {
        if (i % 2) {
            pthread_create(&tids[i], NULL, OddPhilosopher, (void*)(i));
			// 홀수 일때 // 그럼 로직 짤떄 이러면 되는거 아닌가?
        }
        else{
            pthread_create(&tids[i], NULL, Philosopher, (void*)(i));
			//짝수 일
        }
    }

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_join(tids[i], NULL);
    }// 와 이렇게도 가능하구나 충격적이다. 스레드 모드다 끝나야 여기 탈출하겠지.

    for (i = 0; i < NUM_THREADS; i++) {
        pthread_mutex_destroy(&mutexes[i]);
        pthread_cond_destroy(&conditionVars[i]);
    }// 사용한 뮤텍스랑 cond 조건변수를 제거해준다.

    return 0;
}

출처: https://twinw.tistory.com/97 [흰고래의꿈]

0개의 댓글