티스토리 뷰
[Effective Modern C++] 항목 16. const 멤버 함수를 스레드에 안전하게 작성하라
pppgod 2019. 10. 30. 23:57멤버 함수가 멤버 변수들을 수정하지 않는다면 const 로 선언하는 것이 자연스럽다. 그런데 스레드를 사용한다면 문제가 생길 수 있다. 예시를 통해 살펴보자.
mutex의 사용
다음은 다항식의 근을 구는 함수 roots 를 구현하는 상황이다. 성능 향상을 위해 캐싱을 이용하였으며, rootsAreValid 의 값을 이용하여 캐싱 여부를 판별하는 함수이다.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if(!rootsAreValid) {
...
rootsAreValid = true;
}
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
그런데 여기서 두 개의 스레드가 roots 함수를 호출하는 경우를 생각해보자. rootsAreValid 와 rootVals 은 스레드에 안전하지 않기 때문에 mutex 를 써야만 한다. 문제는 roots 함수가 const 함수이기 때문에 mutex 또한 mutable 로 선언해야만 한다.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
std::lock_guard<std::mutex> g(m);
if(!rootsAreValid) {
...
rootsAreValid = true;
}
}
private:
mutable std::mutex m;
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
mutex 를 추가함으로써 이제 Polynomial 객체는 복사와 이동이 불가능해져버렸다.
atomic의 사용
mutex 를 대신하여 atomic 을 사용하는 경우에도 문제가 발생 할 수 있다. 아래와 같은 예제를 보자.
class Widget {
public:
...
int magicValue() const
{
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
atomic 사용함으로써 이제 스레드에는 안전해졌다. 그런데 문제는 동시에 여러개의 스레드가 magicValue() 에 접근하게 되면 비싼 계산을 하는 두 개의 함수들을 모든 스레드가 사용하게 된다. 이는 성능적으로 큰 영향을 끼치게 된다. 이를 막기 위해서는 결과적으로 mutex 를 사용할 수 밖에 없다.
결론
const 멤버 함수는 스레드 환경에서 적절하지 않는 경우가 많다. 현재 스레드에 안전하더라도 앞으로 어떻게 될지 모르기 때문에 언제라도 스레드 환경에 노출될 수 있다는 가정을 하고 작성해야만 한다. const 멤버 함수를 정의하기 전에 항상 주의하도록 하자.
참고 서적
스콧 마이어스, Effective Modern C++
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] 항목 18. 소유권 독점 자원의 관리에는 std::unique_ptr를 사용하라 (0) | 2019.11.03 |
---|---|
[Effective Modern C++] 항목 17. 특수 멤버 함수들의 자동 작성 조건을 숙지하라 (0) | 2019.11.02 |
[Effective Modern C++] 항목 15. 가능하면 항상 constexpr을 사용하라 (0) | 2019.10.29 |
[Effective Modern C++] 항목 14. 예외를 방출하지 않을 함수는 noexcept로 선언하라 (0) | 2019.10.20 |
[Effective Modern C++] 항목 13. iterator보다 const_iterator를 선호하라 (0) | 2019.10.20 |
- Total
- Today
- Yesterday
- Override
- 발아시기
- detach
- 람다
- Modern
- 보편 참조
- Perfect
- std::forward
- forward
- MOVE
- Effective
- C++14
- C
- CPP
- thread
- 다이소
- Forwarding
- auto
- Join
- 포인터
- Unreal
- Overloading
- async
- Future
- const
- C++11
- C++
- std::move
- 보편참조
- Effective Modern C++
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |