ini 파일 형식으로 된 설정 파일을 읽을 때 사용한다.

[ZoneServer]
CHANNEL_COUNT = 10
CHANNEL_USER_COUNT = 200


  • 헤더 파일
1
#include "Poco/Util/IniFileConfiguration.h"
  • 사용 예

ini 파일 읽기에 실패하는 경우 에외가 발생하므로 예외 처리를 해야 한다(예외 종류에 따라서 실패 이유를 알 수 있다).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try
{
    Poco::AutoPtr<Poco::Util::IniFileConfiguration> pZoneServerConf(new Poco::Util::IniFileConfiguration("test.ini"));
    m_ZoneConfig.nMaxChannelCount       = pZoneServerConf->getInt( "ZoneServer.CHANNEL_COUNT" );
    m_ZoneConfig.nMaxChannelUserCount   = pZoneServerConf->getInt( "ZoneServer.CHANNEL_USER_COUNT" );
}
catch ( Poco::FileNotFoundException e ) // 파일이 없는 경우
{
    SERVER_LOG("Init", LOG_LEVEL::error, "%s", e.displayText().c_str());
    return false;
}
catch ( Poco::NotFoundException& e )    // 설정 키워드가 없는 경우
{
    SERVER_LOG("Init", LOG_LEVEL::error, "%s", e.displayText());
    return false;
}


by 흥배 2013.11.11 08:00

POCO 라이브러리를 다운로드 한 후 압축을 풀면 루트 디렉토리에 VC 버전에 맞는 .cmd 파일이 있음. 

예를들면 VC++(VS 2012)의 경우 build_vs110.cmd 파일을 실행한다. 


실행하면 각 라이브러리 디렉토리마다 VS 프로젝트 파일이 생성된다. 



다양한 라이브러리 중 사용할 라이브러리 디렉토리에 들어간 후 VC 버전에 맞는 프로젝트 파일을 열어서 빌드하면 된다.
보통 Foundation, Util, XML, Net 디렉토리의 라이브러리를 빌드한다.
빌드를 하면 32비트 버전은 lib 디렉토리에, 64비트 버전은 lib64 디렉토리에 lib 파일을 생성한다.



lib 파일 빌드가 끝나면 이것을 사용할 프로젝트에서 헤더파일과 lib 디렉토리를 추가한다.


헤더 파일을 추가할 때 해당 디렉토리의 include 디렉토리까지 포함한다. 

예) Foundation의 경우 Foundation\include 

by 흥배 2013.09.13 08:00

사용법 및 소개 http://ikpil.com/1057 
(일본어) https://sites.google.com/site/boostjp/tips/scope_guard 
(일본어) C++11 람다 대응 http://d.hatena.ne.jp/faith_and_brave/20111125/1322199381 
(일본어) this 캡쳐하기 http://d.hatena.ne.jp/faith_and_brave/20120703/1341298054 


  • 스코프 내의 모든 변수를 캡쳐하기(C++11 전용)

BOOST_SCOPE_EXIT_ALL을 사용한다. 클래스의 멤버 변수도 사용할 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CPacketSend kPacketSend( SAD_REP_CHECK_ACCOUNT );
BYTE nErrorCode = E_SAI_REP_CHECK_ACCOUNT::FLAG_CHECK_OK;
     
BOOST_SCOPE_EXIT_ALL(&)
{
    if( nErrorCode != E_SAI_REP_CHECK_ACCOUNT::FLAG_CHECK_OK )
    {
    kPacketSend << nErrorCode;
    kPacketSend << nClientSocketIndexInLoginServer;
    kPacketSend << dwLoginUniqueSocketID;
    kPacketSend << iLoginServerNumber;
    GET_NETWORK_INTERFACE()->SendToServer( iServer, kPacketSend);
    }
};
  • BOOST_SCOPE_EXIT를 사용할 때 조심할 점

이것을 사용하는 스코프 내에서 BOOST_SCOPE_EXIT를 정의하기 전에 return 문을 사용하면 안된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
void Check()
{
   ...........
   if( ... )
   {
     return;
   }
 
   // 아래가 호출되지 않는다
   BOOST_SCOPE_EXIT_ALL(&)
   {
   }
}

올바르게 사용하려면 아래와 같이 바꾸어야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Check()
{
   ...........
   // 아래가 호출되지 않는다
   BOOST_SCOPE_EXIT_ALL(&)
   {
   }
    
   ....
   if( ... )
   {
     return;
   }
 
    
}


by 흥배 2013.09.10 08:00

경고 나오지 않도록 #pragma warning( disable : 경고 번호 )

경고 다시 나오도록 #pragma warning( default : 경고 번호 )

경고가 에러가 되도록 #pragma warning( error : 경고 번호 )

반복되는 경고가 한번만 나오도록 #pragma warning( once : 경고 번호 )

by 흥배 2013.09.06 08:00

struct X

{

    std::mutex m;

    int a;

    std::string b;

};

 

void foo(X& a,X& b)

{

    a.m.lock();

    ....

           a.m.unlock();

          

           b.m.lock();

           ....

           b.m.unlock();

}

 

위의 코드에서

X x1, x2;

인스턴스를 생성 후

스레드 1에서 foo( x1, x2 )를 호출하고 동시에 스레드 2에서 f00( x2, x1 )를 호출하면 데드락 상황에 빠지게 됩니다.

 

위 코드는 작아서 이런 실수를 하지 않겠지만 몇 천, 몇 만 코드에서 여러 프로그래머가 같이 작업을 하다 보면 실수를 할 확률이 아주 높고, 문제가 발생하면 쉽게 찾기도 힘들기도 합니다.

 

이 문제를 방지하기 위해서 std::lock 이라는 함수가 있습니다. std::lock 함수를 사용하면 여러  뮤텍스를 동시에 락을 걸 수 있습니다. 아래와 같이

 

std::lock( a.m, b.m );

 

정의는 아래와 같습니다.

template<class L1, class L2, class... L3>

   void lock(L1&, L2&, L3&...);

  

 

 

보통 좀 더 안전하게 뮤텍스를 사용하기 위해서 보통 unique_lock과 같이 사용하는 편입니다.

 

void foo(X& a,X& b)

{

    std::unique_lock<std::mutex> lock_a(a.m,std::defer_lock);

    std::unique_lock<std::mutex> lock_b(b.m,std::defer_lock);

   

           std::lock(lock_a,lock_b);

 

    // 공유 자원을 사용한다

}

 

 

 

by 흥배 2013.08.26 08:30

std::mutex lock 멤버 함수의 설명을 보면 lock을 호출한 함수에서 unlock을 호출하지 않은 상태에서 또 다시 lock을 호출하면 알 수 없는 동작을 한다고 되어 있습니다.

 

스레드에서 lock을 호출한 후 다시 lock을 호출하는 경우는 아래와 같은 경우입니다.

 

class buffer {

    list<int> queue;

    std::mutex mut;

public:

    bool empty() {

        std::lock_guard<std::mutex> lock(mut);

        return queue.empty();

    }

    ...

    int pop() throw(out_of_range) {

        std::lock_guard <std::mutex> lock(mut);

        while (empty())

        {   

……..

        }

        int tmp = queue.front();

        queue.pop_front();

        return tmp;

     }

};

코드 출처 : http://d.hatena.ne.jp/hidemon/20081218/1229555003

 


위의 buffer 클래스를 보면 pop() 함수를 호출하면 먼저 mutex로 락을 건 다음 empty() 함수를 호출하는데 empty도 같은 mutex를 사용하여 lock을 걸고 있습니다. 즉 이미 락을 건 mutex에 또 lock을 걸고 있습니다. 이런 경우 데드락 상황에 빠질 수가 있습니다. 이 문제를 풀기 위해서는 recursive_mutex를 사용합니다.

recursive_mutex는 같은 스레드에서 lock을 건 후 또 다시 lock을 걸어도 괜찮습니다. lock을 건 횟수만큼 꼭 unlock을 호출해야 합니다.

 

그럼 아래는 위의 buffer 클래스를 recursive_mutex를 사용하여 올바르게 수정한 것 입니다. 단순하게 std::mutex std::recursive_mutex로 바꾸면 됩니다.

(std::mutex std::recursive_mutex의 사용 방법은 같습니다)

 

class buffer {

    list<int> queue;

    std::recursive_mutex mut;

public:

    bool empty() {

        std::lock_guard<std::recursive_mutex> lock(mut);

        return queue.empty();

    }

    ...

    int pop() throw(out_of_range) {

        std::lock_guard <std::recursive_mutex> lock(mut);

        while (empty())

        {   

……..

        }

        int tmp = queue.front();

        queue.pop_front();

        return tmp;

     }

};

 

 

by 흥배 2013.07.01 08:00

앞 선 글 중 ‘std::thread - 2. Join’에서 join을 호출하지 않은 경우 에러 창이 발생하였습니다.


 

이것은 C++11 thread 사양에 의해서 thread 오브젝트가 파괴될 때 join이나 detach를 호출하지 않은 경우 std::terminate를 호출하여 프로그램을 종료하도록 되어 있기 때문입니다.

 

< 예제. 9 >

#include <thread>

#include <iostream>

 

int main()

{

           int nThreadRunCount = 0;

          

           {

                      std::thread Thread = std::thread( [&] ()

                       {

                          for( int i = 0; i < 10; ++i )

                          {

                                ++nThreadRunCount;

                                                               

                                std::cout << "Thread1 ID : " << Thread.get_id() << std::endl;

                          }

                       } );

           }

 

           getchar();

           return 0;

}

 


<예제.9>를 실행하면 에러가 발생하고 아래의 콜스택을 보면



thread 오브젝트의 소멸자에서 terminate를 호출하는 것을 알 수 있습니다.



이것으로 thread에 대해서 기능 위주로 간단하게 살펴 보았습니다. 앞으로 공부해야 할 것들이 더 있어서 남은 것들을 설명 후 다시 thread 사용에 대해서 깊게 들어 가겠습니다.

 

다음부터는 병렬 프로그래밍에서 절대 빠질 수 없는 동기화 객체에 대해서 설명하겠습니다.

 

 

by 흥배 2013.05.28 08:00

sleep_for sleep_until 함수는 스레드를 일시 중지 시킬 때 사용하는 것입니다.

이 두 함수의 차이는 이름에서 쉽게 알 수 있는데 sleep_for는 특정 시간 동안 일시 중지 시키고 싶을 때(. 6초 동안 중지) 사용하고, sleep_until는 어떤 시간이 될 때까지(. 1410분까지) 중지 시킬 때 사용합니다.

 

sleep_for sleep_until 함수는 thread의 멤버는 아니고 this_thread 네임 스페이스에 속해 있습니다.

 

< 예제. 8 >

#include <iostream>

#include <chrono>

#include <thread>

 

int main()

{

           std::cout << "sleep_for 테스트 시작" << std::endl;

   

           std::chrono::seconds dura( 3 );

   

 

           std::chrono::system_clock::time_point StartTime = std::chrono::system_clock::now();

 

           std::this_thread::sleep_for( dura );

   

           std::chrono::system_clock::time_point EndTime = std::chrono::system_clock::now();

 

           std::chrono::seconds sec = std::chrono::duration_cast<std::chrono::seconds>(EndTime - StartTime);

          

           std::cout << "경과 시간()" << sec.count() << std::endl;

 

 

 

          

           std::cout << "sleep_until 테스트 시작" << std::endl;

 

           StartTime = std::chrono::system_clock::now();

 

           std::chrono::seconds dura_sec( 5 );

           std::this_thread::sleep_until( StartTime + dura_sec );

          

           EndTime = std::chrono::system_clock::now();

 

           sec = std::chrono::duration_cast<std::chrono::seconds>(EndTime - StartTime);

          

           std::cout << "경과 시간()" << sec.count() << std::endl;

}

 

< 실행 결과 > 


 

yield는 스레드가 자신에게 할당 되어 있는 시간을 포기하고 다른 실행 스레드에게 처리를 양도합니다.

std::this_thread::yield();

by 흥배 2013.05.20 08:00

VC10에서 병렬 프로그래밍 라이브러리인 Concurrency Runtime 이라는 것이 추가되었습니다.

Concurrency RuntimeOS의 스레드 관리 보다 좀 더 지능적으로 관리를 하며, Concurrency Runtime Windows 7에서 새로 추가된 UMS(User Mode Scheduler)를 사용하고 있습니다.

 

Windows7 이상의 OS에서 64비트 프로그래밍을 한다면 UMS를 사용하는 것이 좋습니다. 그런데 UMS를 사용하는 방법에 대해서 잘 나온 것이 없습니다. 그래서 UMS를 알아도 활용할 방법이 애매한데 이 문제를 해결하는 방법은 Concurrency Runtime의 스케쥴러에서 스레드를 만드는 것입니다. 기존의 스레드를 만드는 방법과 거의 비슷해서 손 쉽게 이전 코드에서 수정도 가능합니다.

 

 

사용하는 방법은 MSDN의 예제를 참고하면 됩니다.

http://msdn.microsoft.com/en-us/library/vstudio/ee624185.aspx

 

제가 간단하게 설명하자면....

아래의 헤더 파일을 포함합니다

#include <concrt.h>

 

네임스페이스를 선언합니다.

using namespace Concurrency;

 

스레드에서 호출할 함수를 정의합니다.

void __cdecl MyThreadFunction(LPVOID param);

 

스레드에 넘겨줄 데이터를 정의합니다.

typedef struct MyData {

    int val1;

    int val2;

    event signal;

} MYDATA, *PMYDATA;

 

 

Concurrency Runtime의 스케쥴러에서 스레드를 생성합니다.

CurrentScheduler::ScheduleTask(MyThreadFunction, pData);

 

 

위의 MyData 구조체에서 event signal 이 사용되는 경우는 만약 메인 스레드에서 생성한 워커 스레드가 종료 될 때까지 대기하고 싶을 때 사용하면 좋습니다.

메인 스레드에서 아래와 같이 하면 워커 스레드가 종료될 때까지 대기 합니다.

pData->signal.wait();

 

워커 스레드는 스레드가 종료될 때 아래와 같이 해서 이벤트를 발생하여 메인 스레드의 대기를 풀어줍니다.

pData->signal.set();

 

 

by 흥배 2013.03.04 08:00

멀티 코어 시대인 만큼 요즘은 프로그램들을 평가할 때 멀티코어를 얼마나 잘 활용하나를 중요하게 따집니다. 그래서 프로그래밍 언어나 라이브러리에서 병렬 프로그래밍 지원을 하는 경우가 많습니다. C++11에서도 큰 특징 중의 하나가 thread 라이브러리 지원입니다.

 

VC의 경우는 현재 사용 중인 10에서는 MS 독자의 Concurrency Runtime을 지원하였고, 이번 버전에서는 C++ 표준의 thread 라이브러리를 제공합니다(VC 전용으로 GPGPU 프로그래밍을 위해 AMP 라이브러리도 제공합니다).

 

C++11 thread 라이브러리를 사용하면 기존에 복잡하고, 플랫폼 마다 다르게 기술해야 하는 스레드 프로그래밍을 쉽고, 플랫폼 독립적으로 구현할 수 있습니다.

 

앞으로 몇 번에 걸쳐서 C++11 thread를 다룹니다. 내용이 어렵지 않으니 천천히 따라오시면 됩니다. thread 라는 것이 무엇인지는 책이나 인터넷을 통해서 미리 공부하시기 바랍니다. 따로 thread에 대한 이론 설명을 하지 않겠습니다. ^^;

 

 

 

thread 생성하기

 

thread 라이브러리를 사용하기 위해서는 아래의 헤더 파일을 포함해야 합니다.

#include <thread>

 

thread 클래스의 생성자는 아래와 같습니다.

thread() _NOEXCEPT;

 

template<class Fn, class... Args>

   explicit thread(Fn&& F, Args&&... A);

 

thread(thread&& Other) _NOEXCEPT;

 

 

인자 중 Fn은 생성된 스레가 호출할 함수(애플리케이션에서 정의한), A Fn에 넘겨 줄 인수 리스트, Other는 기존의 스레드 오브젝트 입니다.

 

첫 번째 생성자는 thread 오브젝트는 만들지만 실제 스레드(OS에서 만들어진 스레드)와는 연결되지 않습니다.

두 번째 생성자는 실제 스레드와 연결된 오브젝트를 생성합니다. 만약 충분한 리소스가 없어서 스레드를 시작할 수 없다면 system_error 오브젝트에 resource_unavailable_try_again 에러코드로 예외를 발생시킵니다.

세 번째 생성자는 다른 thread 오브젝트를 넘겨 받아서 오브젝트를 생성합니다. 인자가 우측 값 참조 이므로 이후에 Other에 연결된 스레드는 Other과 연결이 끊어집니다.

 

아래의 예제를 통해서 스레드를 어떻게 사용하는지 보겠습니다.

 

< 예제. 1 >

#include <thread>

#include <iostream>

 

int main()

{

           std::thread Thread1( [] ()

                        {

                                   for( int i = 0; i < 5; ++i )

                                   {

                                          std::cout << "Thread Num : " << i << std::endl;

                                   }

                      } );

 

 

           std::thread Thread2;

           Thread2 = std::thread( [] ()

                        {

                                    for( int i = 10; i < 15; ++i )

                                   {

                                          std::cout << "Thread Num : " << i << std::endl;

                                   }

                         } );

 

           std::thread Thread3 = std::thread( [] ( int nParam )

                        {

                                 for( int i = 20; i < 25; ++i )

                                 {

                                    std::cout << "Thread Parameter : " << nParam << std::endl;

                                 }

                            }, 4 );

 

          

 

           getchar();

           return 0;

}

 

< 결과 >


위 결과를 보면 스레드가 3개 만들어져서 실행된 것을 알 수 있습니다. 그런데 첫 번째 줄에 출력된 결과가 이상하죠? 이것은 공유 객체를 동기화 하지 않아서 발생한 것입니다. 이것은 뒤에 동기화 객체를 설명할 때 고쳐 보겠습니다^^;




ps: 위의 소스는 완벽한 것이 아닙니다. 아마 실행하면 에러가 발생할 것입니다. 이와 관련된 글은 뒤에 적을 글에서 설명할 예정이라서 이번 글의 코드에서는 일부러 조금 불완전한 코드를 사용했습니다.^^;;;


by 흥배 2012.11.12 09:00
| 1 2 3 4 5 6 |