unique_lock는 쉽게 말하면 lock_guard에 기능이 더해진 것이라고 볼 수 있습니다.

lock_guard의 경우 정의와 동시에 락을 걸고, 파괴될 때만 락을 풀 수 있습니다.

 

그에 비해 unique_lock은 정의와 동시에 락을 걸 수도 있고, 또는 뒤에 락을 걸 수도 있습니다. 또 락을 푸는 것도 원하는 대로 조절이 가능하며 try_lock이나 try_lock_for, try_lock_until 등도 지원합니다. 그리고 unique_lock끼리 소유한 뮤텍스 객체를 서로 교환도 할 수 있으며, 뮤텍스 객체를 얻거나 해제할 수도 있습니다.

 

간단한 경우에는 lock_guard가 사용하기 편하지만 뮤텍스를 다양하게 사용하고 싶은 경우에는 unique_lock이 훨씬 더 좋습니다.

 

 

unique_lock 생성

unique_lock 클래스의 생성자는 다양한 방법으로 뮤텍스를 사용할 수 있도록 여러 버전이 준비 되어 있습니다.

 

unique_lock() noexcept;

unique_lock(unique_lock&& Other) noexcept;

explicit unique_lock(mutex_type& Mtx);

unique_lock(mutex_type& Mtx, adopt_lock_t Adopt);

unique_lock(mutex_type& Mtx, defer_lock_t Defer) noexcept;

unique_lock(mutex_type& Mtx, try_to_lock_t Try);

template<class Rep, class Period>

   unique_lock(mutex_type& Mtx,

      const chrono::duration<Rep, Period> Rel_time);

template<class Clock, class Duration>

   unique_lock(mutex_type& Mtx,

      const chrono::time_point<Clock, Duration> Abs_time);

unique_lock(mutex_type& Mtx,

   const xtime *Abs_time) noexcept;

 

unique_lock의 사용 예는 아래와 같습니다.

 

std::mutex m;

....

// 락을 만들고 동시에 뮤텍스의 소유권을 가진다

std::unique_lock lck(m, std::adopt_lock);

 

 

std::mutex m;

....

// 락을 만들지만 뮤텍스의 소유권은 가지지 않는다

std::unique_lock lck(m,std::defer_lock);      

...

if (lck.try_lock()) {

....

}                   

 

 

std::unique_lock<std::timed_mutex>

        lk(m,std::chrono::milliseconds(3));

....

if(lk) {

    process(data);

}                   

 

 

 

unique_lock 멤버 함수

 

- void lock();

- mutex_type *mutex() const _NOEXCEPT;

- explicit operator bool() _NOEXCEPT

- unique_lock& operator=(

    unique_lock&& Other

  ) _NOEXCEPT;

- bool owns_lock() const _NOEXCEPT;

- mutex_type *release() _NOEXCEPT;

- void swap(

    unique_lock& Other

  ) _NOEXCEPT;

- bool try_lock() _NOEXCEPT;

- template<class Rep,

    class Period>

  bool try_lock_for(

     const chrono::duration<Rep,

     Period>& Rel_time

  );

- template<class Clock, class Duration>

    bool try_lock_until(const chrono::time_point<Clock, Duration>& Abs_time);

    bool try_lock_until(const xtime *Abs_time);

- void unlock();

 

저작자 표시
신고
by 흥배 2013.07.15 08:00

detach 함수는 thread 오브젝트에 연결된 스레드를 떼어냅니다.

그래서 detach 이후에는 thread 오브젝트로 스레드를 제어할 수 없습니다. 그러나 detach를 호출했다고 해서 관련된 스레드가 종료 되는 것이 아닙니다. thread 오브젝트와 스레드의 연결이 끊어지는 것입니다.

 

 

< 예제. 5 >

#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;

                                          }

                              } );

 

           while( nThreadRunCount < 3 )

           {

           }

 

           Thread.detach();

 

           //Thread.join();

 

           getchar();

           return 0;

}

 

< 실행 결과 >


 

<예제. 5>의 결과를 보면 detach 이후 스레드 id가 초기화 됨을 알 수 있습니다. 그리고 위 예제에서 join() 메소드를 주석 처리하고 있는데 만약 주석을 풀면 아래와 같은 에러가 발생합니다.



 

이렇게 thread 오브젝트가 스레드를 가지지 않는데 join 함수를 호출하면 에러가 발생하는 것을 막기 위해서는 joinable 함수를 사용합니다. joinable 함수를 호출하여 반환 값이 true인 경우에만  join 함수를 호출하면 위와 같은 에러를 막을 수 있습니다.

 

< 예제. 6 >

#include <thread>

#include <iostream>

#include <mutex>

 

int main()

{

           std::mutex mtx_lock;

 

           int nThreadRunCount = 0;

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

                                {

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

                                {

                          ++nThreadRunCount;

                                                               

                                  mtx_lock.lock();

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

                                  mtx_lock.unlock();

                                }

                          } );

 

           while( nThreadRunCount < 3 )

           {

                     mtx_lock.lock();

                     std::cout << "Thread Is Joinable : " << Thread.joinable() << std::endl;

                     mtx_lock.unlock();

           }

 

           Thread.detach();

 

           std::cout << "Thread Is Joinable : " << Thread.joinable() << std::endl;

 

           getchar();

           return 0;

}

 

< 실행 결과 3>

 

 

저작자 표시
신고
by 흥배 2013.05.03 08:00

프라우드넷을 직접 보기 전까지는 순수하게 네트웍 기능만 지원하는 걸로 알고 있었는데 도움말과 샘플코드를 보니 네트웍 이외의 기능도 있다는 것을 알 수 있었습니다.

 

제가 가장 반가운 기능이 '오류 덤프 시스템' 기능 이었습니다.

 

게임 개발을 해 보신 분들은 알겠지만 싫든 좋든 개발 도중이나 서비스 중에 꼭 프로그램이 크래쉬가 발생하는 것을 볼 수 있습니다. 이때 가장 첫 번째 해야 할 일은 크래쉬가 되었을 때 빨리 덤프 파일을 얻고 분석 하는 것입니다.

그런데 덤프 파일을 얻으려면 따로 코딩을 해야 하고 특히 발생하는 덤프 파일을 원하는 장소로 수집하는 기능도 만들어야 합니다.

다행히 이런 기능은 오픈 소스에 있기도 합니다. 그런데 영어에 익숙하지 않으면 가져와서 사용하기가 불편할 수도 있습니다.

 

그런데 프라우드넷에서 크래쉬 덤프 수집 기능을 제공해서 한글로 된 도움말을 보고 쉽게 사용할 수 있고 혹시 이상한 부분이 있을 때 물어볼 곳이 있어서 좋을 것 같더군요.

 

도움말과 샘플 코드를 보니 일반적으로 미니 덤프를 만드는 기능과 이 덤프 파일을 특정 위치로 전송하는 기능도 있었습니다. 또한 크래쉬가 발생하면 미니 덤프를 만들고 다시 자동으로 서버를 실행하는 기능도 있었습니다.

개발 도중이나 서비스 도중 서버가 죽었을 때 누군가 재빨리 재 시작을 시켜주기 어렵다면 자동으로 재 실행 해주는 기능은 편리할 것 같습니다. 다만 기능을 조금 더 만들어서 특정 시간 대에 여러 번 크래쉬가 일어나면 재 시작을 하지 않는 기능도 필요할 것 같습니다(잘못하면 미친 듯이 미니 덤프가 생기겠죠^^;)

 

네트웍 기능과는 상관 없지만 게임 개발에 있어서 꼭 필요한 크래쉬 덤프 수집 기능은 이런 기술을 아직 가지고 있지 않은 곳에서는 꼭 필요하니까 잊지 않기 바랍니다.^^ 

저작자 표시
신고
by 흥배 2013.02.07 08:00

프라우드넷을 조금씩 보고 있는데 현재까지 크게 문제 되는 것은 만나지 못했는데 약간 아쉬운 부분은 있더군요.

 

1. 빠르지 않은 콜렉션

프라우드넷 설명서를 보면 프라우드넷에서 제공하는 콜렉션에 대한 글을 볼 수 있습니다

모든 콜렉션 라이브러리를 제공하지 않고 우리가 자주 사용하는 것들을 제공하고 있고 속도도 빠르다고 나와 있습니다. 그러나 제가 테스트해 본 결과 그냥 사용하면 기존 라이브러리 보다 더 느린 경우도 있고, 옵션을 조절하면 조금 빠른 정도의 성능 밖에 나오지 않았습니다.

문의를 해보니 앞으로 성능 개선을 할 계획을 세울 거라고 하더군요. 개인적으로 혹시나 하는 마음이 있었는데 아쉬웠습니다.^^;


개인적인 생각으로 프라우드넷에서 사용하는 콜렉션은 내부에서만 사용하는 것으로 단정하고 설명서에서 이것을 사용하면 성능에 큰 도움을 준다는 느낌을 주는 문구를 진짜 개선 될 때까지는 빼는 것이 좋지 않을까 생각합니다(현재는 있어봐야 오해만 될 것 같습니다)

 



2. VS2012 미지원

제가 받은 평가판에서는 아직 2012를 지원하지 않더군요. 문의해보니 2012 버전은 다 만들었는데 아직 내부 테스트 중이라고 하더군요.

아마 프라우드넷 소스를 가지고 있는 분들은 자체적으로 빌드를 하면 되니(현재 VS2010은 지원하니까) 전혀 문제가 안되고 저처럼 lib 파일을 받는 사람에게는 아쉬운 부분이 될 것 같습니다.

VS2012도 정식 버전이 출시한 지 8개월이 되어 가고 이전부터 RC나 베타, RTM 등이 나와서 입수는 빨리 할 수 있으니 최신 버전을 빨리 지원해 주면 좋을 것 같습니다

 

저작자 표시
신고
by 흥배 2013.02.06 08:00

VC10에서 선보였던 C++11의 기능 중 강력하면서 사용하기 쉽고, 자주 사용한 기능이 아마 'auto'이지 않을까 생각합니다. 예전에 강연을 할 때 auto와 관련된 예제를 보여드리면 많은 분들이 아주 좋아하시더군요(좀 놀라기도 하시더군요^^). 어떤 분들은 딴 건 제쳐두고 이것 때문이라도 VC10을 사용해야겠다는 분들이 있었습니다.

 

이번 VC11에서도 'auto'와 같은 강력한 기능이 있습니다. 바로 'range base for' 입니다. 이것을 사용하면 반복문을 아주 쉽고, 강력하게 사용할 수 있습니다.

VC 특화 기능인 for each와 비슷하기 때문에 기존에 for each를 사용하고 있다면 이제는 range base for로 쉽게 바꾸어서 사용하면 됩니다.

 

 

예제를 통해 일반적인 for , VC for each, range base for문의 차이를 예제를 통해서 보겠습니다.

 

< 예제. 1 >

#include <iostream>

  

int main()

{

           int NumberList[5] = { 1, 2, 3, 4, 5 };

 

          

           std::cout << "일반적인 for " << std::endl;

          

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

           {

                     std::cout << i << std::endl;

           }

 

 

           std::cout << "VC++ 특화의 for each" << std::endl;

 

           for each( int i in NumberList )

           {

                     std::cout << i << std::endl;

           }

 

 

           std::cout << "range base for " << std::endl;

 

           for( auto i : NumberList )

           {

                     std::cout << i << std::endl;

           }

 

           return 0;

}

 


< 실행 결과 >


 


<예제.1>을 보면 일반적인 for 문은

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

와 같이 시작과 종료 조건, 증가 값 이렇게 3개의 조건에 의해서 반복 됩니다.

 

그러나 range base for문은 VC만의 반복문인 for each와 비슷하게 데이터셋 변수와 이 데이터셋 요소의 타입을 선언하면 됩니다.

for( auto i : NumberList )

 

기존의 for 문에 비해서 또는 for each 보다도 간편해졌고, for each는 표준이 아닌 VC만의 기능인 것에 비해서 range base for C++ 표준 기능입니다.

 

range base for 문의 문법은 아래와 같습니다.

for ( for-range-declaration : expression ) statement

 

 

range base for 덕분에 반복문의 사용이 쉬워졌고, for 문을 사용할 때 종료 조건이 잘못되어 메모리 침범을 하는 위험도 피할 수 있게 되었습니다.

저작자 표시
신고
by 흥배 2012.08.29 09:00

unique_ptr shard_ptr를 사용하려면 먼저 아래처럼 헤더 파일을 추가해야 합니다.

#include <memory>

 

 

struct TEST

{

        TEST() {}

           ~TEST() {}

};

 

 

1. unique_ptr 동적 배열 사용

std::unique_ptr<TEST[]> pTest( new TEST[2] );

템플릿 부분에 동적 배열임을 알려줘야 합니다.

 

 

2. shard_ptr 동적 배열 사용

std::shared_ptr<TEST> pTest( new TEST[2], std::default_delete<TEST[]>() );

unique_ptr과 조금 다릅니다. 템플릿 부분에는 일반적인 타입을 선언해주면 됩니다. 다만 deleter 부분에 동적 배열임을 선언해줘야 합니다.

 

 

3. unique_ptr 사용 중 할당된 객체의 메모리 해제

std::unique_ptr<TEST> pTest( new TEST );

                    

pTest.reset(nullptr);

 

unique_ptr이 소멸되기 전에 할당된 객체의 메모리를 해제 할 때는 reset을 사용하면 간편합니다.

원래 reset은 다른 객체로 바꿀 때 사용하는 것인데 이런 용도로 사용해도 좋습니다

 

 

 

< 테스트 코드 >

#include <memory>

#include <iostream>

 

 

struct TEST

{

           TEST()

           {

                     std::cout << "TEST 생성자" << std::endl;

           }

 

           ~TEST()

           {

                     std::cout << "TEST 소멸자" << std::endl;

           }

};

 

 

 

int main()

{

           std::cout << "unique_ptr - 동적 배열 사용" << std::endl;

           {

               std::unique_ptr<TEST[]> pTest( new TEST[2] );

           }

           std::cout << std::endl;

 

 

           std::cout << "shared_ptr - 동적 배열 사용" << std::endl;

           {

               std::shared_ptr<TEST> pTest( new TEST[2], std::default_delete<TEST[]>() );

           }

           std::cout << std::endl;

 

 

           std::cout << "unique_ptr - reset 사용" << std::endl;

           {

               std::unique_ptr<TEST> pTest( new TEST );

                    

               std::cout << "unique_ptr - reset 사용 전" << std::endl;

               pTest.reset(nullptr);

               std::cout << "unique_ptr - reset 사용 후" << std::endl;

           }

 

           return 0;

}




 

 

저작자 표시
신고
by 흥배 2012.08.20 09:00
| 1 |

티스토리 툴바