[Linux] Thread Programming

정재훈·2022년 4월 7일
0

Linux

목록 보기
12/19

void *

어떤 타입의 주소도 모두 다 저장할 수 있는 만능 포인터이다. 주소를 저장만 할 수 있지 사용할 수는 없다.
int * / char * => void *

void 포인터를 사용하기 위해서는 다음 코드와 같이 작성하면 가능하다.

#include <stdio.h>

int main()
{
	int x = 0;
	char t = 'A';

	void* p1 = &x;
	void* p2 = &t;

	// printf("%d\n", *p1); // 에러
	printf("%d\n", *(int*)p1);   // 0
	printf("%c\n", *(char*)p2);  // A

	return 0;
}

일반적인 포인터 사용

#include <stdio.h>

void abc(int* p)
{
	printf("%d\n", *p); // 10
}

int main()
{
	int x = 10;
	abc(&x);

	return 0;
}

void 포인터 사용

#include <stdio.h>

void abc(void *v)
{
	int* p = (int*)v;
	printf("%d\n", *p);  // 10
}

int main()
{
	int x = 10;
	abc(&x);

	return 0;
}

Thread Programming

함수 단위로, 2개 이상의 함수를 동시에 실행시키고 싶을 때 사용한다.

pthread LIB

#include <pthread.h> 를 통해 링크할 수 있고 해당 라이브러리에서 사용할 수 있는 명령어를 알아보자.

thread를 사용하기 위해서는 선언해줘야 합니다. pthread_t [변수명]으로 선언합니다.

create & join

create

pthread_create([thread id 주소], [쓰레드 설정] , [실행할 함수명], [파라미터])

  • thread id 주소 : thread id 변수 주소
  • 쓰레드 설정 : 거의 NULL이다.
  • 파라미터 : 함수에 인자 값을 전달해주고 싶을 때 사용

join

join을 만나면 쓰레드가 종료됨을 기다린 후, 커널 내부 정리 작업을 한다.
pthread_join([thread id], [thread 리턴값])
pthread_create를 사용하였다면 pthread_join을 통해 정리작업을 해주자.

#include <stdio.h>
#include <unistd.h>  // sleep 라이브러리
#include <pthread.h>

void *abc() // 함수 Thread
{
    while(1)
    {
        printf("ABC\n");
        sleep(1);  // 1초 대기
    }

    return 0;
}

void *bts()
{
    while(1)
    {
        printf("BTS\n");
        sleep(1);  // 1초 대기
    }

    return 0;
}

int main()
{
    pthread_t t1, t2;   // t1,t2 thread 선언

    pthread_create(&t1, NULL, abc, NULL); // thraed craete
    pthread_create(&t2, NULL, bts, NULL);

    pthread_join(t1,NULL); // thread join
    pthread_join(t2,NULL);

    return 0;
}

빌드 방법 : gcc [소스코드 파일명] -o [실행 파일명] -lpthread

결과 : 무작위로 1초마다 ABC와 BTS를 먼저 출력한다.

사용된 쓰레드는 3개로 abc함수 , bts 함수 , main 함수로 main 함수의 쓰레드는 while문이 thread에서 계속돌기 때문에 join에 위치하게 된다.

Thread 인자 값 넘기기

#include <stdio.h>
#include <unistd.h> // sleep 라이브러리
#include <pthread.h> // therad 라이브러리

void *abc(void *p)  // void * 로 주소 받기
{
    int *a = (int *)p;  // void*는 저장만 가능하고 사용불가로 int*로 바꾸어주기

    while(1)
    {
        printf("#%d\n",*a);   // #1 무한 출력
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int gv = 1;

    pthread_create(&tid, NULL, abc, &gv); // 파라미터로 gv 주소 넘기기
    pthread_join(tid,NULL);

    return 0;
}

인자 넘겨줄 때 에러 유발 코드

오류 코드

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

pthread_t tid[4];

void *run(void *arg) // 모두 같은 변수 i의 주소로 i는 for문을 통해 마지막으로 4라는 값이 저장된다.
{
    int a = *(int*)arg;
    printf("%d",a);  // 윈도우 OS 때문에 오류가 안날수도 있지만 원래는 4444가 출력되는 오류
}

int main()
{
    for(int i=0; i<4; i++)
    {
        pthread_create(&tid[i],NULL,run,&i); // 4개의 thread에게 변수 i의 주소를 넘겨준다.
    }
    for(int i=0; i<4; i++)
        pthread_join(tid[i],NULL);

    return 0;

}

해결(정상) 코드

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

pthread_t tid[4];

void *run(void *arg) 
{
    int a = *(int*)arg;
    printf("%d",a);  // 0~3까지 랜덤 순서로 출력된다 => 0132 중복 x
}

int main()
{
	int id[4];  // 4개의 id 변수를 만들어준다.
    for(int i=0; i<4; i++)
    {	
    	id[i] = i; // 서로 다른 4개의 id 변수에 값 넣기
        pthread_create(&tid[i],NULL,run,&id[i]); //4개의 id변수는 주소가 서로 다르다. 
    }
    for(int i=0; i<4; i++)
        pthread_join(tid[i],NULL);

    return 0;
    

구조체 넘겨주기

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

typedef struct ABC  // 구조체 정의
   {
       int a;
       int b;
       char c;
   }TH;

void *abc(void *go)
{
   TH* pp = (TH*) go; // 구조체 받기
   // 구조체 나누기
   int x = (*pp).a;
   int y = (*pp).b;
   char z = (*pp).c;

   while(1)
   {
       printf("%d %d %c\n", x,y,z);
       sleep(1);
   }

}

int main()
{
   TH go = {10,20,'F'}; // 구조체 선언 및 초기화

   pthread_t tid;

   pthread_create(&tid,NULL,abc,&go);
   pthread_join(tid,NULL);

   return 0;
}

Thread

Thread는 메모리를 공유할까?

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

void *abc(void *arg)
{
   int value = *(int *)arg;
   int *mm1 = (int *)malloc(4);
   int *mm2 = (int *)malloc(4);

   printf("=====================\n");
   printf("0x%X\n", mm1);
   printf("0x%X\n", mm2);
   printf("0x%X\n", &value);
	// 출력 결과를 확인하면 Thread는 서로 다른 메모리 주소를 가진다는 것을 확인할 수 있습니다.
}

int main()
{
   int five = 5;
   int six = 6;

   pthread_t t1, t2;

   int *mm1 = (int *)malloc(4);
   int *mm2 = (int *)malloc(4);

   printf("0x%X\n", mm1);
   printf("0x%X\n", mm2);

   pthread_create(&t1, NULL, abc, &five);
   pthread_create(&t2, NULL, abc, &six);

   pthread_join(t1, NULL);
   pthread_join(t2, NULL);

   return 0;
}

결론
Thread는 서로 메모리를 공유하고 있지 않습니다.
하지만 static 변수는 공유합니다.

Thread id 확인 법

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

void *abc(void *arg)
{
    pthread_t id = pthread_self(); // id 확인법 2
    printf("%u\n",id);
}

int main()
{
    pthread_t pt[3];

    for(int i=0; i<3; i++)
    {
        pthread_create(&pt[i],NULL,abc,NULL);
        printf("%u\n", pt[i]);  // id 확인법 1
    }

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

    return 0;
}

Race Condition

Race Condition : Thread/Process의 타이밍에 따라 결과 값이 달라질 수 있는 상태
Critical Section : Race Condition이 되어버리는 소스코드 영역들

Race Condition 유발 코드

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

pthread_t tid[4];
int cnt;

void *run()
{
   for (int i=0; i<10000; i++) cnt++;
}

int main()
{
   for(int i =0; i<4; i++) 
   {
  		pthread_create(&tid[i], NULL, run, NULL);
       // usleep(100);  : 성능하락 및 유지보수 측면에 좋지 않은 간단 해결책 , #include <unistd.h> 사용
   }
   for(int i=0; i<4; i++) pthread_join(tid[i],NULL);

   printf("%d\n",cnt); // 40000이 나와야하지만, 더 낮은 값이 나오게 됩니다.

   return 0;
}

근본 해결 : Mutex(뮤텍스)

동기화 : Critical Section에 동시에 수행하지 않도록 않게 하기 위해 Thread 간 협의를 맞추는 것

mutex 사용법

  1. pthread_mutex_t [함수명] : mutex 선언
  2. pthread_mutex_init([mutex 함수명],NULL) : mutex 객체 초기화
  3. pthread_mutex_destory : 객체 제거
  4. pthread_mutex_lock(&[mutex 함수명]) : mutex lock 요청
  5. pthread_mutex_unlock(&[mutex 함수명]) : mutex lock 요청 반환
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t mlock; // mutex 선언
pthread_t tid[4];
int cnt;

void *run()
{
    pthread_mutex_lock(&mlock); // mutex lock 요청
    for (int i=0; i<10000; i++) cnt++;
    pthread_mutex_unlock(&mlock); // mutex lock 요청 반환
}

int main()
{
    pthread_mutex_init(&mlock, NULL); // mutex 객체 초기화

    for(int i =0; i<4; i++)
    {
        pthread_create(&tid[i], NULL, run, NULL);
    }
    for(int i=0; i<4; i++) pthread_join(tid[i],NULL);

    printf("%d\n",cnt);  // 40000으로 정상 출력된다.

    return 0;
}
profile
여러 방향으로 접근하는 개발자

0개의 댓글