본문 바로가기
728x90

c++59

[MultiThread] Event, Condition Variable ■ 스핀락, 일반 Lock, 이벤트 · 앞서 배운 예제로 Spinlock은 기내 화장실 앞에서 죽치고 쭈욱 기다리는 것이고, 일반 Lock은 내 자리에 돌아갔다가 다시 화장실이 빌 것 같을 때 자리가 있는지 확인하러 오는 방식이다. · 이벤트 방식은 승무원에게 화장실이 비게 되면 알려달라고 부탁한 뒤 우리는 자리로 가서 대기하는 것이다. ■ 사용법 (1) #include 헤더를 추가한다. (2) HANDLE 타입의 변수를 선언한다. - 여태까지 변수 선언을 하면 유저레벨에서 관리를 하던 변수들이었지만 - Handle은 운영체제가 관리하는 오브젝트를 만들어주게끔 요청할 수 있는 커널 오브젝트(Kernel Object)이다. 커널 오브젝트 (Kernel Object) 구성 요소 모든 커널 오브젝트는 사용되어.. 2024. 4. 8.
[MultiThread] Mutex, Spinlock, DeadLock ■ atomic의 한계 변수 하나만을 보호한다는 한계가 있음. 즉, 함수 내에 여러 공유 변수 전체를 보호할 수 없음. 또한 변수를 보호하더라도 load, store와 같이 대입 및 저장 같은 단순한 연산을 보호하는 것이고 동적할당을 하며 메모리를 옮겨다니는 벡터와 같은 컨테이너는 100% 보호 가능하다는 보장을 할 수가 없음. ■ 동적 할당에서의 멀티쓰레딩 아래 코드를 동작시키면 크래쉬가 발생한다. 그 이유는 앞서 배운 벡터의 원리를 생각하면 이해가 된다. Capcity를 들고있는데 Size가 Capacity와 같아진다면 벡터는 더 넓은 capacity를 차지하기 위해 모든 메모리를 이사시킨다. 이때 다른 쓰레드가 벡터에 접근하게 된다면 이사하고나서 메모리 주소가 바뀌었음에도 불구하고 이제는 사용하지 .. 2024. 4. 8.
[MultiThread] 공유 자원, Atomic ■ 공유데이터 사용 시 발생할 수 있는 문제 상황 아래와 같이 코드를 작성해봤다. 이를 실행 했을 때 결과값은 어떻게 될까? 논리적으로 생각하면 아래와 같이 0이 되어야 한다. 하지만 만약 백만번씩 실행한다고 했을 때 여전히 우리가 생각하는대로 0이 될까? 이를 코드 수정 후 확인해보면 아래와 같이 엉뚱한 값이 나오는 것을 볼 수 있었다. 위에서 공유 영역인 Data영역에 sum 변수가 할당되었다. 이 공유 영역에 할당된 sum 변수를 thread1과 thread2가 같이 사용하기 때문에 발생한 문제이다. 아무리 그래도 더하고 빼는 횟수는 같아야할 텐데 이러한 문제는 왜 발생하는가? 이유는 어셈블리를 까보면 ++ 혹은 --하는 코드는 아래 세 단계로 이루어져 있기 때문이다. [int operator++ .. 2024. 4. 7.
[MultiThread] 캐시 및 CPU 명령어 파이프라인 ■ CPU와 RAM 왔다갔다 계속 사용하기엔 시간이 다소 많이 소요되니 이 둘 사이에 저장공간인 레지스터 및 캐시를 이용한다. [CPU 코어 구성] (1) ALU (연산장치) (2) 캐시 장치 - 레지스터 - L1 캐시 - L2 캐시 .... 캐시 장치를 구성하는 레지스터, L1캐시, L2캐시는 계층 구조로 생각해봤을 때 아래로 갈수록 (Register->L2) 저장 공간은 많아지지만 속도는 느려진다. 위 캐시 장치들을 모두 훑어봤을 때도 데이터가 없을 경우에 주기억장치로 가서 훑어보게 된다. ■ 캐시 정책 지역성 설명 시간지역성 (Temporal Locality) (레스토랑 비유) 시간적으로 보면, 방금 주문한 테이블에서 추가 주문이 나올 확률이 높다. 방금 주문한걸 메모해 놓으면 편하지 않을까? 공간.. 2024. 4. 7.
[MultiThread] 쓰레드 생성 ■ C++ 11 이전 및 이후 이전에는 각 운영체제마다 제공하는 함수가 다르기 때문에 문서를 찾아보고 해야했다. 하지만, C++ 11(모던C++)이후 부터는 쓰레드 생성이 표준에 추가되어 #include 를 통해 굉장히 편리하게 스레드를 만들 수 있게 되었다. ■ 사용 법 (1) #include 헤더를 추가한다. (2) 일반적인 변수를 선언하듯이 std::thread t; 와 같이 스레드를 선언한다. (3) 다만, Thread가 처음 실행되는 순간에는 실행이 되어야 하는 함수를 우리가 넣어줘야 한다. (4) 하지만 위 코드를 그대로 사용하면 크래쉬가 난다. 그 이유는 - 메인 스레드가 종료가 되었는데 자식 스레드가 끝나지 않았기 때문에 크래쉬가 일어난다. - 그러므로 메인 스레드는 자식 스레드가 끝날 때.. 2024. 4. 7.
[Modern C++] weak_ptr, unique_ptr ■ shared_ptr의 한계점 shared_ptr k1(new Knight()); shared_ptr k2(new Knight()); k1->_target = k2; k2->_target = k1; 위와 같이 서로를 참조하고 있을 때 둘의 Ref Count는 각각 2가 된다. 애당초 ref count가 0이 되어야 메모리를 해제(delete)시키는데 k1이 소멸되어 ref count를 -1한다고 해도 ref count는 여전히 1이 되어버린다. 그러면 결국엔 서로를 추적하고 있는 상황에선 메모리 해제가 일어나지 않는 버그가 발생하므로 메모리 릭(Memory Leak)이 일어난다. ▼ Ref Count가 0이 안되어서 소멸자가 호출이 안되는 상황 이런 상황에선 임의로 우리가 어떤 유저가 로그아웃을 해가지.. 2024. 3. 31.
[Modern C++] shared_ptr ■ 기존 포인터의 문제 동적 할당을 하고 메모리 해제(delete)를 하지 않으면 메모리 릭(Memory leak)이 일어나서 언젠가 메모리를 모두 고갈하여 프로그램이 터지는 일이 발생할 수 있다. 이를 자동으로 관리해주기 위해 template 문법을 사용하여 우리가 임의로 wrapper 클래스를 만들어 이용할 수도 있다. 이럴 경우 Wrapper클래스가 소멸될 때 자동으로 _ptr가 담고있는 주소를 따라들어가 k1 메모리를 해제할 것이다. 하지만 객체를 하나가 아니라 다수가 참조하고 있을 때 이런 Wrapper클래스를 사용하고 있어도 아래와 같은 문제가 발생할 수 있다. [기본 포인터의 문제 예] 화살을 쏴서 화살이 목적지에 도달하고 있는데 목적지가 delete 되었을 때 등등 참조해야하는 메모리가 중.. 2024. 3. 31.
[Modern C++] rvalue-ref (오른값 참조) ■ Modern C++ C++11로 넘어오면서 auto, lambda, ravlue-ref(오른값 참조) 이 삼총사가 큰 변화이다. auto, lambda 같은 경우에는 없어도 우리가 기존에 하던 방식으로 잘 개발 할 수 있어 새로운 기능보단 편의성이 증대한 것인데 오른값 참조는 없던 개념이 새로 생긴거여서 이로 인해 C++자체가 업그레이드 되고 더 빨라졌다. 그래서 C++11에 새로 추가된 문법 중 우선순위 1이나 다름없다. ■ 왼값(l-value) vs 오른값(r-value) (1) l-value : 단일식을 넘어 계속 지속되는 개체 (2) r-value : l-value가 아닌 나머지 기본적으로 오른쪽에 있으니 오른값이라고 생각할 수 있으나 정확히는 그것이 아니다. ■ 복사 생성자 vs 복사 대입 .. 2024. 3. 31.
[Modern C++] Unicode, MBCS, WBCS ■ ASCII char는 C++에서 1바이트를 차지한다. unsinged 라면 0~255 사이의 숫자만 표현할 수 있다. 컴퓨터 출시 초기에 작업을 할 때는 미국에서 군사용 목적으로 사용되었고 이때 ASCII코드를 사용했었다. 그러므로 이는 영어만 고려되어있는 체계인 것이다. 아스키 코드는 0~127사이의 숫자만 사용하고 있고 이는 7bit인 것을 알 수 있다. [출력 예시] const char* test = "aa유진현"; cout 2024. 3. 29.
728x90