운영체제

[게임 프로그래머 입문 올인원] 멀티쓰레드 프로그래밍 : 스마트 포인터

순정법사 2024.12.05

A. 스마트 포인터

1. 멀티스레드와 스마트 포인터

a. 객체 포함 레퍼런스 카운트 블록

💥 일반 포인터를 사용하면 안되는 이유

주소값을 진짜 사용해도 되는지 안되는지에 대한 정보가 없어서 

포인터를 그냥 들고있으면 생명 주기가 꼬여서 날라가고... 난리남

 

그래서 레퍼런스 카운트를 사용해 주소값을 관리하게 됨 

객체에 포함, 레퍼런스 카운트를 관리하는 블록 2가지로 만들 수 있음

 

1) 객체에 포함하는 refCount

 

블록을 만들어주고

 

나중에 삭제하더라도 releaseref로 작동

 

 

위 순간 주소값을 기억하는 애가 하나 증가함

위와같이 카운팅을 쳐서 관리하는 방법이 있음 (수동 레퍼런스 카운트)

 

위를 멀티스레드 환경에서 작업할 시 알아야 할것

 

=> 증감연산자 -> 아토믹으로 해결

 

 

여기서 아토믹만 붙인다고 끝? -> 스마트 포인터를 사용해야 함

 

위 상황에서 멀티스레드 환경일때 괜찮을까?

 

위는 멀티스레드 환경에서만 위험!!

 

b. 수동 관리의 위험성

Knight가 AddRef()일때 위 코드가 먼저 실행되지 않고

ReleaseRef()한다면 0-> -1이 되면서 없어지게 되어 오류

 

그래서 생명보장이 받지 않은 상태에서 

*****가 끼어들어서 뭐가 실행되게 되면 안전하지 않음!

 

 

위와같이 수동으로 관리해야하는건 정말 비효율적이다

 

위를 이해하고 난 후 스마트 포인터 공부!

 

 

2. 스마트포인터

a. 스마트포인터와 레퍼런스 카운팅 블록

이제 위와같이 객체안에 넣지 않고 따로 관리

 

약간 이런 모습, 객체와 카운팅을 따로 관리하고 이를 스마트포인터가 가리키는 형식으로 제작됨

 

2) 레퍼런스 카운팅 블록 (표준 tsharedptr 방법)

 

TSharePtr 클래스를 만들어서 관리하자

 

 

이렇게 만들어준다음 

 

 

기본생성자에서 바로 1을 증가시키도록 만들어줌 (수동아님)

=> 궁극적인 목표  RefCount 클래스를 통해 주소를 자동으로 카운팅해주는 것

 

복사를 해도 원래 데이터 값을 그대로 유지함

 

이런 느낌으로 진행됨

 

그 뒤로 참조파일을 끌어와서 위 클래스를 완성시킴

 

++ 아래는 참조파일 추가한 코드

 

복사, 이동시 자기 자신을 폭파시킴

 

b. 스마트 포인터 규칙

이제 생포인터가 눈에 띄면 안됨

 

 

이렇게 KnightRef를 만들어주고 (모든걸 관리)

 

한번은 만들어주고 (new Knight())

이제 이건 절대 스마트포인터로만 사용된다

 

 

Test함수를 실행하면 복사가 되면서 카운팅 +1  이된다

 

만약 중간에 끼어들어가서 (멀티스레드) 나이트를 소멸시키면 어떻게 될까? 

=> 어떤 식으로 하건 knight가 존재하면 괜찮다

 

근데 스마트 포인터를 수동으로 만든 위 방식에선 나이트 생성 후 어디 넣어주면 2가 되기 때문에 카운팅 2

 

따라서 1을 한번 소멸시켜줘야 함 (knight->ReleaseRef)

이건 따로 만들어준 방법을 사용하기에 하는것임

 

이게 표준 스마트 포인터 사용법

 

카운팅 1 증가시키는 것 자체가 복사비용이 부담스러울 수 있음 (아토믹 사용)

잠시만 사용하고 싶을 땐 어떻게 할까? 👉 & 사용

 

 

참조값으로 받아서 사용하면 복사비용이 없다

 

-> 이것도 스마트포인터를 들고있어서 중간에 삭제시킬수가 없음

 

스마트 포인터를 직접 저장하는 위와같은 상황이 발생할 수도 있는데 위와 같은 상태에서는 사용 X => 이런 상황 자체가 거의 없음

 

c. 스마트포인터의 소멸과 사이클

최종적으로 소멸은 어디서 이루어지냐?

-> 알 수 없음.. 누군가가 막타를 친다 >< (그래도 생포인터보단 ㄱㅊ)

 

순환 문제(사이클)

스마트 포인터의 순환(사이클) 문제가 발생하면 어떤 문제가 일어날까?

-> 리크 포인터를 사용

객체의 생명주기에는 영향을 주지 않고 레프카운트 블록만 유지하는 역할 

 

d. This 와 스마트포인터

스마트 포인터를 사용할 때 발생하는 애매한 상황

 

 

위와같이 기사가 이동하는 기능이 있다고 함

다른사람을 대상으로 이동하기는 괜찮은데, 내 자신을 움직이려면 (this)를 사용하기 어려움

 

This는 애당초 ref카운팅이 들어가지 않는 일반 포인터 

일반 포인터를 대상으로 sharedptr을 사용한 꼴

 

 

따라서 포인터의 카운트는 1이고, 포인터를 관리하는 애는 this가 되어버림

따라서 포인터 자체가 소멸되어 버리니 문제가 발생됨

 

이 코드를 실행해주면 오류가 나는 걸 알 수 있음

 

이중으로 삭제했기 때문에 오류가 남!

 

그림으로 설명해보자

 

노란색 : 나이트 객체, 주황색 : 레퍼 카운트 블록 , 초록색 : 스마트 포인터

 

나이트와 레퍼런스 카운트 블록은 묶어서 관리가 되고

초록색인 스마트포인터는 두 블록을 묶을 객체를 따로 관리하는 것

 

근데  this 포인터를 넘겨주면서 새로운 스마트포인터를 생성해

아래 묶음을 삭제시키면서 객체 (노란색)까지 터져버리는 것 

 

따라서 위와같은 이유로 인해 꼭 꼼꼼하게 사용을 해야합니다~!

 

해결방법

 

따라서 위 코드를 사용하려면

 

이와 같이 weakptr 을 사용해 생명 주기에 영향을 주지 않는 포인터를 사용해야 함 

Or

Enable_ shared_from_this

 

이 코드를 사용해야 한다

 

 

 


 

출처 : https://www.inflearn.com/course/%EA%B2%8C%EC%9E%84-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EC%9E%85%EB%AC%B8-%EC%98%AC%EC%9D%B8%EC%9B%90-rookiss#curriculum

 

[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버

Rookiss | 어디부터 시작할지 막막한 게임 프로그래밍 입문자를 위한 All-In-One 커리큘럼입니다. C++, 자료구조/알고리즘, STL, 게임 수학, Windows API, 게임 서버 입문으로 이어지는 알찬 커리큘럼으로

www.inflearn.com