공유된 자원에 여러 개의 프로세스가 동시에 접근하는 현상을 막고자 전 시간엔, Atomic을 배웠다.
하지만 Atomic의 단점을 복습해보자면 연산이 느리고, 어디에서든 사용해선 안된다고 하였다.
그 어디에서든이 어떤 상황인지 C++ 코드를 통해 알아보자.
atomic<vector<int>>av;
void Push() {
for (int32 i = 0; i < 10'000; i++)
{
av.push_back(i)
}
}
가령 STL vector를 사용한다고 했을 때, 해당 STL은 모두 싱글 쓰레드 기준으로 만들어진 라이브러리다 보니
에러가 발생한다. 이 해당 에러에 대해선 바로 다음에 설명하겠고, 여하튼 atomic을 사용해서 vector의 push_back을
사용하고 싶지만, 위 코드에서는 문법 에러가 날 것이다.
즉 Atomic에서 지원하지 않는 코드가 될 수 있다.
곧바로, STL은 싱글 쓰레드 기준으로 만들어졌다고 언급하였는데, 간단히 vector의 원리를 이해해보면 조금 이해하기 쉽다
vector의 경우 reserve를 따로 하지 않고 위 코드 처럼, push_back을 통해 Capacity를 늘리게 되고
이 때, 기존에 할당 된 Memory에서 더 큰 Memory를 할당 후 이사하게 되는데 이 과정에서 아래와 같은 현상이 일어나
크래쉬가 나타난다.
먼저 1번 쓰레드가 Vector에 접근하여 capacity가 부족해서 이사할 곳으로 이사 후 기존 vector의 메모리를 삭제하게 된다.
그 와중에 삭제된 곳에 Thread2 가 접근하니 크래쉬가 난다고 볼 수 있다.
물론 vector의 reverse함수로 미리 큰 메모리를 할당 시켜주면 크래쉬는 나지 않겠지만, 멀티 쓰레드의 공유 자원 접근 시 일어날 수 있는 문제에 대해서는 저번 시간에 언급 하였 듯, 데이터가 정상적으로 들어가지 않는 문제가 발생한다.
이를 해결하기 위해서 Mutex ( Mutal Exclution 상호 배제 ) 기법을 사용할 수 있다.
원리는 되게 간단하다. 1번 쓰레드가 일이 끝날 때 까지 다른 쓰레드가 접근 하는 것을 lock을 통해 막아두고
자기 일이 끝나면 unlock을 통해 다른 쓰레드가 접근 할 수 있도록 허용한다.
이 때, 다른 쓰레드는 lock이 걸려 있다면, 대기하다가 unlock이 되면 일을 진행한다.
사용법은 정말 간단하다.
mutex m;
void push() {
for (int32 i = 0; i < 10'000; i++)
{
m.lock();
v.push_back(i);
m.unlock();
}
}
위 코드와 같이 임계 영역 ( Critical Section)을 정해두고 공유 자원에 접근하는 쓰레드들이 서로 겹치지 않게
각각 단독으로 실행할 수 있도록 한다.
'프로그래밍-기본기 > C++' 카테고리의 다른 글
(쓰레드) Spin Lock (0) | 2023.09.25 |
---|---|
(쓰레드) Atomic (0) | 2023.09.06 |
스마트 포인터 (1) | 2023.09.05 |