공부중
[C/C++] pthread 동기화에 관하여. 본문
pthread 동기화에 관하여 글을 적어보려고 한다.
일단 동기화 내용 하나 없는 아래의 코드를 보자.
#include <iostream>
#include <pthread.h>
int g_value = 0;
void *ADD(void* data)
{
for (int i = 0; i < 100000; i++)
{
g_value++;
}
return nullptr;
}
int main()
{
const int threadCount = 2;
pthread_t pthreads[threadCount];
for (int i = 0; i < threadCount; i++)
{
pthread_create(&pthreads[i], NULL, ADD, nullptr);
}
for (int i = 0; i < threadCount; i++)
{
pthread_join(pthreads[i], nullptr);
}
std::cout << g_value << std::endl;
return 0;
}
위의 코드는 2개의 스레드를 생성해서 ADD함수를 각각의 스레드가 돌아가는데
기대하는 출력값은 100,000회의 2번이니까 200,000이 되어야 하겠지만...
매번 실행할때마다 값도 다르고 의도한 값인 200,000이 될때도 있겠지만 되지않는 경우도 있다.
왜 저러한 일이 발생하냐면 간단하게 적자면 동기화가 일어나지 않은 경우이고
좀 더 자세히 풀어보자면
타임라인 | 1번 스레드 | 2번 스레드 |
1 | 값 읽기 ( 0 ) | waiting... |
2 | 값 증가 ( 1 ) | waiting... |
3 | waiting... | 값 읽기 ( 0 ) -> 아직 쓰지 않았으므로 0으로 읽음 |
4 | waiting... | 값 증가 ( 1 ) |
5 | waiting... | 값 쓰기 ( 1 ) |
6 | 값 쓰기 ( 1 ) | .... |
... | ..... | ..... |
각각의 스레드가 언제 접근할지도 모르는 상태이고 위의 예시처럼 접근 시점이 겹쳐 쓰기 전에 덮어씌워지기 때문에 기대한 값인 200,000이 되지 않는 경우가 발생하는것이다.
위와같은 문제가 발생하지 않도록 동기화를 해주어야 한다. 아래의 코드를 보자
#include <iostream>
#include <pthread.h>
int g_value = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *ADD(void* data)
{
for (int i = 0; i < 100000; i++)
{
pthread_mutex_lock(&mutex);
g_value++;
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main()
{
const int threadCount = 2;
pthread_t pthreads[threadCount];
for (int i = 0; i < threadCount; i++)
{
pthread_create(&pthreads[i], NULL, ADD, nullptr);
}
for (int i = 0; i < threadCount; i++)
{
pthread_join(pthreads[i], nullptr);
}
std::cout << g_value << std::endl;
pthread_mutex_destroy(&mutex);
return 0;
}
동기화 방법중 하나인 뮤텍스(Mutex)를 사용한 동기화 코드이다.
뮤텍스는 커널(운영체제) 영역에서 동작하므로 느릴수 있지만 안정적인 동기화 방법중 하나이다.
동시에 접근하지 못하도록 막는 상호배제 방식으로 동기화를 진행한다.
lock / unlock 이름대로 들어갈때 문잠그고 나올때 문따고 이런식으로 이해하면 될듯하다.
다른(?) 방법은 또 아래와 같다.
#include <iostream>
#include <pthread.h>
#include <semaphore.h>
int g_value = 0;
sem_t semaphore;
void *ADD(void* data)
{
for (int i = 0; i < 100000; i++)
{
sem_wait(&semaphore);
g_value++;
sem_post(&semaphore);
}
return nullptr;
}
int main()
{
sem_init(&semaphore, 0, 1); // 동시에 1개의 스레드만 접근 가능하도록...
const int threadCount = 2;
pthread_t pthreads[threadCount];
for (int i = 0; i < threadCount; i++)
{
pthread_create(&pthreads[i], NULL, ADD, nullptr);
}
for (int i = 0; i < threadCount; i++)
{
pthread_join(pthreads[i], nullptr);
}
std::cout << g_value << std::endl;
sem_destroy(&semaphore);
return 0;
}
지금 여기서는 단 2개의 스레드만 사용중이지만 init할때 몇개의 스레드가 접근 가능할지 설정 가능하며
sem_init (sem_t * sem, int pshared, unsigned int value);
가운데 pshared가 뭔지 찾아봤는데...
이 세마포어를 프로세스의 스레드 간에 공유할지, 아니면 프로세스 간에 공유할지 여부를 결정한다는것 같다.
0을 전달하면 같은 프로세스의 다른 스레드에서 액세스할 수 있는 세마포어를 얻게 됩니다. 라고 한다
https://stackoverflow.com/questions/1291566/sem-init-what-is-the-pshared-parameter-for
뮤텍스는 락에 소유권이 있어서 락을 획득한 스레드만이 락을 해제 가능하지만
세마포어는 락의 소유권이 없고 누구나 세마포어를 해제 가능하다.
또 다른 방법이 있는지 pthread말고 다른 스레드에는 동기화 방법에 어떠한것들이 있는지 좀 찾아보고 글을 올려봐야겠다.
'Programing > C, C++' 카테고리의 다른 글
[C++] 생성자 호출순서 다시 돌아보기 (0) | 2024.11.27 |
---|---|
[C++] std::sort 사용시 invalid comparator가 발생한 경우. (0) | 2024.01.21 |
[VC++]권한이 필요한 프로세스를 생성할 경우. (0) | 2022.09.25 |
[C/C++] 배열 안에 자료형 크기가 다른 값으로 채우기 (0) | 2022.04.18 |
[MSVC]public 생성자를 만들었음에도 불구하고 링크에러가 나는 경우 (0) | 2021.05.24 |