C++ 11부터 지원되는 Range-based for(범위 기반 for)는 기존의 C++ STL의 컨테이너, 배열 등에서 반복문을 좀 더 쉽게 사용하게 해주는 기능이다.

 

이 기능은 Microsoft의 Visual C++ 기능인 for each문과 그 기능과 사용법이 비슷해서 쉽게 사용할 수 있다.

 

기존의 for문과 iterator를 이용한 방법과 Visual C++ for each문을 이용한 방법 그리고 C++ 11 표준의 Range-based for문을 이용한 방법을 비교하자면 아래와 같다.

 

vector<Object*> vect;

// 기존의 for문과 auto 키워드를 이용한 iterator 반복 처리
for (auto obj = vect.begin(); obj != vect.end(); obj++)
{
    // Object에 처리
}

// Visual C++ 타입의 for each문을 이용한 반복 처리
for each(auto obj in vect)
{
    // Object에 처리
}
// C++ 11타입의 Range-based for문을 이용한 반복 처리
for (auto obj : vect)
{
    // Object에 처리
}

 

코드를 살펴보면 분명하게 미세하게나마 코드의 양이 줄어드는 것이 보인다. 같은 동작을 하는 데도 기존의 for문과 auto 형식의 복잡한 타이핑 없이 처리할 수 있다는 것은 프로그래머의 피로도를 상당히 줄여준다.(물론 auto 키워드가 나오기 전에는 더더욱 복잡했었다.)

 

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

'C++' 카테고리의 다른 글

[C++11] enum class  (1) 2017.07.17
[C++ 11] static_assert  (0) 2017.05.23
[C++ 11] Auto Vectorization  (1) 2016.11.01
[C++ 11] Scoped Lock  (0) 2016.11.01

Multi thread 프로그래밍을 할때 현재 thread가 작업하는 메모리를 다른 thread에 덮어쓰거나 잘못 사용하는 일이 발생하지 않도록 주의해야한다.

이러한 문제를 대비하기 위한 기법이 바로 thread 동기화인데 Critical Section, Semaphore, Mutex 등이 있다.

이러한 것들을 사용하지 않는다면

 

Tread 동기화를 사용하지 않은 스레드 작업

#include <iostream>
#include <Windows.h>
#include <thread>
#include <memory>
#include <mutex>
using namespace std;
int iArr[500];      // 두 thread가 작업할 메모리 공간

void test1()    // test1 함수는 iArr에 0을 채운다.
{
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 0;
        Sleep(1);
    }
}

void test2()    // test2 함수는 iArr에 1을 채운다.
{
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 1;
        Sleep(1);
    }
}

int main()
{
    // 각 함수를 스레드로 작동
    thread t1(test1);
    thread t2(test2);

    // 두 함수가 끝나기를 기다린다.
    t1.join();
    t2.join();

    // 결과값 출력
    for (int i = 0; i < 500; i++)
    {
        cout << iArr[i];
        if ((i +1) % 50 == 0)
        {
            cout << endl;
        }
    }
}

 

 

위의 결과 처럼 중간 중간 값을 덮어씌워서 오작동을 일으킬 수 있다.

 

하지만 위에서 말한 thread 동기화 기법을 사용하면 이 문제는 해결된다.

 

 

 

Mutex를 사용한 thread 작업

#include <iostream>
#include <Windows.h>
#include <thread>
#include <memory>
#include <mutex>
using namespace std;

int iArr[500];
mutex m;    // Thread Lock을 걸 Mutex 클래스

void test1()
{
    m.lock();   // 메모리에 lock을 걸어 다른 thread에서 사용하지 못하게 한다.
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 0;
        Sleep(1);
    }
    m.unlock(); // 메모리에 대한 작업이 끝난 이후에 lock을 해제한다.
}

void test2()
{
    m.lock();
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 1;
        Sleep(1);
    }
    m.unlock();
}

int main()
{
    // 각 함수를 스레드로 작동
    thread t1(test1);
    thread t2(test2);

    // 두 함수가 끝나기를 기다린다.
    t1.join();
    t2.join();

    // 결과값 출력
    for (int i = 0; i < 500; i++)
    {
        cout << iArr[i];
        if ((i +1) % 50 == 0)
        {
            cout << endl;
        }
    }
}

 

 

Mutex를 사용하면 처음 lock을 건 thread에서 작업이 끝난 이후에야 다른 thread에서 그 메모리에 접근해서 작업이 가능하기 때문에 모든 배열에 1이 출력이 된다.

 

 

 

 

이렇게 별 문제가 없어 보이는 thread lock에는 문제가 있는데 바로 Race Condition이다. 이 race condition은 일반적으로 1번 thread와 2번 thread가 있고 데이터가 담긴 메모리 A, B가 있을 때,  1번 thread가 A메모리에 lock을 건 상태에서 B메모리에 작업을 하려고 하고, 2번 thread는 B메모리에 lock을 건 상태에서 A메모리에 작업을 하려고 할때 발생한다. 이렇게 되면 1번 thread와 2번 thread는 서로 자신의 작업이 끝나야 각각의 메모리의 lock 해제하는데 서로에게 lock이 걸린 메모리 때문에 작업을 그 이후로 진행할 수 없기 때문에 프로그램은 작동을 정지하고 만다. 이런 상황을 다른 말로 dead lock, 교착상태라고도 한다.

 

하지만 scoped lock은 사실 이런 완벽한 교착 상태를 예방하기 위한 것이라기 보다는 사소한 프로그래머의 실수를 방지하기 위한 것이다. 다음의 코드를 보자.

 

프로그래머의 실수!

#include <iostream>
#include <Windows.h>
#include <thread>
#include <memory>
#include <mutex>
using namespace std;
int iArr[500];
mutex m;
void test1()
{

    // 이 프로그래머는 훌륭하게 lock을 걸었지만
    m.lock();
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 0;
        Sleep(1);
    }
    // unlock을 까먹고 하지 않았다!
}

void test2()
{
    m.lock();
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 1;
        Sleep(1);
    }
    m.unlock();
}

int main()
{
    thread t1(test1);
    thread t2(test2);
    t1.join();
    t2.join();
    for (int i = 0; i < 500; i++)
    {
        cout << iArr[i];
        if ((i +1) % 50 == 0)
        {
            cout << endl;
        }
    }
}

 

위의 코드처럼 실수로 thread lock을 건 이후에 작업이 끝나고 unlock을 까먹는다면 2번 thread는 1번 thread가 걸어둔 lock이 해제되기를 영원히 기다릴 것이다. 위 코드처럼 간단한 thread 예제라면 이러한 문제를 쉽게 찾아내겠지만 수만줄을 넘어가고 여러 종류의 thread가 여러 개 돌아가는 커다란 프로그램에서 저런 기억하기 어려운 사소한 실수를 범하게 된다면 얼마나 긴 시간을 허비하게 될까?

 

 

 

 

그래서 나온 것이 scoped lock이라는 것인데 이것의 원리는 매우 간단하다. 우리가 사용하는 Mutex를 하나의 클래스로 가볍게 감싸는 것이다. 그리고 클래스의 생성자가 실행될 때 lock()을 실행하고 클래스의 소멸자가 실행될 때 unlock()을 실행해 주는 것이다.

이것은 프로그래머가 직접 구현해도 될 정도로 간단한 작업이지만 표준 C++에서는 이미 지원되고 있으니 해당 기능을 사용하면 된다.

 

Scoped Lock 사용법

#include <iostream>
#include <Windows.h>
#include <thread>
#include <memory> // Scoped lock 기능을 사용하기 위해 include 해야하는 header
#include <mutex>
using namespace std;
int iArr[500];
mutex m;
void test1()
{
    lock_guard<mutex> g(m);      
    // lock_guard 클래스의 템플릿에 mutex 클래스를 넣고 생성자에 우리가 사용하는 mutex 객체를 넣어주면 된다.
    // lock_guard 객체가 생성될 때 자동으로 thread에 lock이 걸린다.
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 0;
        Sleep(1);
    }
    // 그리고 함수가 끝날 때 or 스코프를 벗어날 때 자동으로 lock_guard 객체가 소멸되면서 thead lock이 해제된다.
}
void test2()
{
    lock_guard<mutex> g(m);
    for (int i = 0; i < 500; i++)
    {
        iArr[i] = 1;
        Sleep(1);
    }
}
int main()
{
    thread t1(test1);
    thread t2(test2);
    t1.join();
    t2.join();
    for (int i = 0; i < 500; i++)
    {
        cout << iArr[i];
        if ((i +1) % 50 == 0)
        {
            cout << endl;
        }
    }
}

 

 

 

[유니티 어필리에이트 프로그램]

아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Create 2D & 3D Experiences With Unity's Game Engine | Unity Pro - Unity Store

Unity Pro software is a real-time 3D platform for teams who want to design cross-platform, 2D, 3D, VR, AR & mobile experiences with a full suite of advanced tools.

store.unity.com

[투네이션]

 

-

 

toon.at

[Patreon]

 

WER's GAME DEVELOP CHANNEL님이 Game making class videos 창작 중 | Patreon

WER's GAME DEVELOP CHANNEL의 후원자가 되어보세요. 아티스트와 크리에이터를 위한 세계 최대의 멤버십 플랫폼에서 멤버십 전용 콘텐츠와 체험을 즐길 수 있습니다.

www.patreon.com

[디스코드 채널]

 

Join the 베르의 게임 개발 채널 Discord Server!

Check out the 베르의 게임 개발 채널 community on Discord - hang out with 399 other members and enjoy free voice and text chat.

discord.com

 

반응형

'C++' 카테고리의 다른 글

[C++11] enum class  (1) 2017.07.17
[C++ 11] static_assert  (0) 2017.05.23
[C++ 11] Auto Vectorization  (1) 2016.11.01
[C++ 11] Range-Based For  (0) 2016.11.01

+ Recent posts