작성
·
521
1
안녕하세요. 강의 감사히 들었습니다.
TSharedPtr<T>이 thread safe한지에 대해 고민해보던 중 질문이 생겨 글 작성하였습니다. 관련된 다른 질문들을 보아도 의문이 해결되지 않아서요.
class User
{
public:
changeWraight(WraightRef wraight) {
wraight_ = wraight;
}
WraightRef wraight() {
return wraiht_;
}
private:
WraightRef wraight_;
};
User *user = Server::getUser(userId);
user->changeWraitht(wraight);
User *user = Server::getUser(userId);
WraightRef wraightRef = user->wraight();
위 상황에서 thread 2 가 실행되어 복사생성자가 호출되었고, 복사생성자에서 Set함수가 호출되어 아래 조건문 까지 검사하고 thread1이 실행되었다고 가정한다면 WraightRef를 사용하여도 복사시 문제가 발생할 수 있을 것이라고 생각했는데요. 제가 잘못생각한 것이 있다면 피드백 부탁드리고 싶습니다.
inline void Set(T* ptr)
{
_ptr = ptr;
if (ptr) {
ptr->AddRef();
}
}
답변 1
1
미묘한 부분인데요.
결론부터 말씀드리면, 표준 shared_ptr의 경우에
RefCount 자체는 ThreadSafe하지만,
대입/치환 등 set하는 부분은 그렇지 않습니다.
생각하신 바와 같이 안전한게 아니기 때문에
그 부분을 꼼꼼히 챙기기 위해서 추가로 Lock을 걸거나,
그게 귀찮다면 atomic 버전의 shared_ptr가 있습니다.
표준에 아닌 버전은 어떻게 구현을 했는지가 중요한데요.
복사를 한다고 항상 문제가 있는 것은 아니고,
"한번에" 복사가 이루어질 수 있는 지가 중요한데
64비트 운영체제라면 64비트까지가 사실상 원자적으로 복사가 되는 크기입니다.
하지만 스마트 포인터에서 원본객체주소(8바이트)+RefCount블록(8바이트)를
동시에 들고 있는 구조라면, 복사가 더 이상 thread-safe하지 않겠죠.
(왜냐하면 정말 8바이트/8바이트를 각각 복사하는 형태로 코드가 만들어지기 때문)
이럴 경우 위처럼 atomic 버전을 만들거나, lock을 걸거나 별도 처리가 필요합니다.
결과적으로 RefCount 관리하는 부분까지 상속받아 객체를 만들기로 합의된 경우,
원본객체주소와 RefCount블록이 사실 동일한 객체가 되어 복사도 안전하지만
일반적인 shared_ptr라면 그렇지 않습니다.