어떤 타입의 주소도 모두 다 저장할 수 있는 만능 포인터이다. 주소를 저장만 할 수 있지 사용할 수는 없다.
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;
}
함수 단위로, 2개 이상의 함수를 동시에 실행
시키고 싶을 때 사용한다.
#include <pthread.h>
를 통해 링크할 수 있고 해당 라이브러리에서 사용할 수 있는 명령어를 알아보자.
thread를 사용하기 위해서는 선언해줘야 합니다. pthread_t [변수명]
으로 선언합니다.
pthread_create([thread id 주소], [쓰레드 설정] , [실행할 함수명], [파라미터])
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에 위치하게 된다.
#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;
}
#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 변수는 공유합니다.
#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 : Thread/Process의 타이밍에 따라 결과 값이 달라질 수 있는 상태
Critical Section : 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;
}
동기화 : Critical Section에 동시에 수행하지 않도록 않게 하기 위해 Thread 간 협의를 맞추는 것
mutex 사용법
pthread_mutex_t [함수명]
: mutex 선언 pthread_mutex_init([mutex 함수명],NULL)
: mutex 객체 초기화pthread_mutex_destory
: 객체 제거pthread_mutex_lock(&[mutex 함수명])
: mutex lock 요청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;
}