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

병렬 프로그래밍을 해보면 싫든 좋든 여러 스레드에서 하나의 객체에 동시에 접근하는 경우가 발생합니다. 앞선 std::thread에 관한 글을 보면 소개한 예제 중 그런 경우를 보실 수 있을 겁니다.

 

< 예제 >

#include <thread>

#include <iostream>

 

int main()

{

           std::thread Threads1( [] ()

              {

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

                        {

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

                        }

              } );

 

 

           std::thread Threads2;

           Threads2 = std::thread( [] ()

              {

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

                        {

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

                        }

              } );

 

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

              {

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

                      {

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

                      }

              }, 4 );

 

           getchar();

           return 0;

}

 

위 예제에서 ‘std::cout’ 각 스레드에 동시에 접근하고 있습니다. 그래서 실행하면 아래와 같은 결
과가 나옵니다.


실행 화면을 보면 출력 결과 중 일부가 뒤죽박죽 되어 있는 것을 알 수 있습니다. 이유는 Thread1 스레드에서 출력스트림 결과를 다 출력하기 전에 Threads2가 출력스트림을 사용하여 발생한 문제입니다. 이런 것을 data-race(데이터 경합) 이라고 합니다.

 

그런데 위 예제를 여러 번 실행해 보면 아래처럼 올바르게 나올 때도 있습니다.



사실 이런 부분이 병렬 프로그래밍의 어려움 중의 하나입니다. 여러 스레드에서 하나의 공유 자원을 사용할 때 타이밍에 의해서 순서대로 사용할 수도 있고 그렇지 못할 수도 있기 때문에 버그 발생이 언제 어떻게 생길지 알기 힘듭니다.

 

병렬 프로그래밍을 할 때는 언제나 공유 자원을 최소한으로 하고, 공유 자원은 꼭 동기화 객체를 사용하여 여러 스레드가 동시에 사용하지 못하도록 해야 합니다.

 

위의 문제는 동기화 객체를 사용하면 해결 할 수 있습니다. 여기서는 앞선 글에서 사용한 적이 있는 뮤텍스(std::mutex)를 사용하겠습니다.

 

그럼 위 예제를 mutex를 사용하여 문제를 해결해 보겠습니다.

 

<예제 1 >

#include <thread>

#include <iostream>

#include <mutex>

 

int main()

{

           std::mutex mtx_lock;

 

           std::thread Threads1( [&] ()

              {

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

                        {

                                          mtx_lock.lock();

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

                                          mtx_lock.unlock();

                        }

              } );

 

 

           std::thread Threads2;

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

              {

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

                        {

                                          mtx_lock.lock();

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

                                          mtx_lock.unlock();

                        }

              } );

 

           std::thread Threads3 = std::thread( [&] ( int nParam )

              {

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

                      {

                                 mtx_lock.lock();

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

                                 mtx_lock.unlock();

                      }

              }, 4 );

 

          

 

           getchar();

           return 0;

}

 

< 실행 결과>



std::mutex를 사용하기 위해서는 아래의 헤더 파일을 추가합니다.

#include <mutex>

 

스레드에서 공유 자원을 사용할 때는 아래 처럼 락을 걸어서 뮤텍스 mtx_lock의 소유권을 얻어서 다른 스레드가 접근하지 못하도록 합니다. 그리고 공유 자원을 다 사용하였다면 락을 해제하여 mtx_lock의 소유권을 버립니다.

mtx_lock.lock();

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

mtx_lock.unlock();

 

락을 건 후(lock) 락을 풀(unlock) 때까지는 다른 스레드는 대기를 하고, 락을 풀면 대기하고 있는 스레드 중 하나가 락을 건 후 공유 자원을 사용합니다.

그러므로 락을 건 후에는 꼭 락을 풀어야 하고(만약 풀지 않으면 데드락 상황에 빠지게 됩니다), 락을 사용하는 부위가 너무 빈번하면(스레드 대기가 늘어나므로) 병렬 프로그래밍의 장점이 많이 약해집니다.

 

 

try_lock

std::mutex lock unlock 이외에 try_lock 이라는 멤버 함수가 있습니다.

bool try_lock();

 

스레드에서 lock을 호출할 때 다른 스레드가 이미 lock을 호출하여 뮤텍스의 소유권을 가진 후 아직 unlock을 호출하지 않았다면 그 자리에서 대기를 합니다. 그러나 try_lock은 대기를 하지 않고 false를 반환합니다(뮤텍스의 소유권을 얻지 못한 경우). 반대로 true를 반환한 경우는 뮤텍스에 대한 소유권을 가지게 되므로 공유자원을 안전하게 사용할 수 있습니다.

 

try_lock을 사용하는 경우는 만약 공유 자원을 다른 스레드에서 사용 중이면 스레드가 대기하면서 그냥 시간을 소비하지 않고 또 다른 작업을 하도록 할 때 사용하면 좋습니다.

 


 

ps : 참고로 std::mutex Windows OS에서는 내부적으로는 크리티컬섹션을 사용합니다.

 

 

저작자 표시
신고
by 흥배 2013.06.03 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

hardware_concurrency()는 하드웨어 스레드 컨텍스트 수를 알려 주는 함수입니다.

스레드 컨텍스트는 CPU , 코어 수, 하이퍼 스레드와 관계가 있습니다.

 

하드웨어 스레드 컨텍스트 수는 Windows '작업 관리자' '성능' 탭에서 볼 수 있습니다.





제 컴퓨터는 CPU 하나에 코어는 4개이고, 하이퍼 스레딩을 지원하지 않는 CPU라서 CPU 사용 현황에 4개의 그림만 나옵니다. 그래서 저는 hardware_concurrency() 함수를 실행하면 결과는 4가 나옵니다.

 

관례적으로 프로그램에서 멀티 스레드를 만들 때 (하드웨어 스레드 컨텍스트 수 * 2) +1 이라는 공식으로 나온 수 만큼 만들면 좋다고 합니다. 이때 hardware_concurrency() 함수를 사용하면 좋습니다.

그리고 만약 hardware_concurrency()를 사용할 수 없는 경우라면 0 을 반환 합니다.

 

< 예제. 7 >

#include <thread>

#include <iostream>

 

 

int main()

{

           std::cout << "하드웨어 컨텍스트 수 : " << std::thread::hardware_concurrency() << std::endl;

 

           return 0;

}

 

< 실행 결과 >


저작자 표시
신고
by 흥배 2013.05.10 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

get_id() 함수는 thread 객체에 연결된 스레드의 식별자 id를 반환합니다. get_id()를 사용하면 멀티스레드에서 공용 리소스에 접근하는 스레드가 어떤 것인지 알 수 있습니다.

 

swap() 함수는 thread 객체간의 스레드를 서로 교환할 때 사용합니다. STL 컨테이너에 있는 swap과 같은 역할을 한다고 보면 됩니다.

 

get_id() swap()이 어떤 것인지는 아래 예제 코드를 보시면 이해가 쉬울 것 같습니다.

 

< 예제. 4 >

#include <thread>

#include <iostream>

#include <mutex>

 

int main()

{

           // 동기화 객체. 다음에 자세히 설명하겠습니다.

std::mutex mtx_lock;

 

           int nThreadRunCount1 = 0;

           std::thread Thread1;

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

                       {

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

                        {

                                   ++nThreadRunCount1;

                                                                                                                                                                           

                                   mtx_lock.lock();

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

                                   mtx_lock.unlock();

                        }

                     } );

          

           int nThreadRunCount2 = 0;

           std::thread Thread2;

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

                       {

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

                        {

                                   ++nThreadRunCount2;

                                                                                                 

                                   mtx_lock.lock();

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

                                   mtx_lock.unlock();

                       }

                      } );

          

           // Thread1 Thread2가 스레드 ID를 제대로 할당 받은 후 swap하기 위해서 일시 대기

           while( nThreadRunCount1 < 3 && nThreadRunCount2 < 3 )

           {

           }

 

           mtx_lock.lock();

           std::cout << "swap 1,2" << std::endl;

           mtx_lock.unlock();

 

           Thread1.swap( Thread2 );

          

           Thread1.join();

           Thread2.join();

 

           return 0;

}

 

< 결과 >

 


위의 결과를 보면 각 thread 객체마다 스레드 id를 할당 받은 것을 알 수 있습니다. 그리고 swap을 하면 swap 하기 전에 비해서 Thread1 Thread2의 스레드 id가 서로 바뀐 것을 알 수 있습니다.

 

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

CPU의 벡터 레지스터를 사용하는 좁은 의미의 별렬 프로그래밍


다음의 글들을 보면 어떤 것인지 쉽게 이해할 수 있다.



auto vectorization - VC++11  (한글)

http://sydlife.wordpress.com/2012/06/06/auto-vectorization-vc11/



Auto-Vectorizer in Visual Studio 11 ? Did It Work?

http://blogs.msdn.com/b/nativeconcurrency/archive/2012/05/22/auto-vectorizer-in-visual-studio-11-did-it-work.aspx


Auto-Vectorizer in Visual Studio 11 ? How Much Faster?

http://blogs.msdn.com/b/nativeconcurrency/archive/2012/06/26/auto-vectorizer-in-visual-studio-11-how-much-faster.aspx 


Auto-Vectorizer in Visual Studio 11 ? Cookbook

http://blogs.msdn.com/b/nativeconcurrency/archive/2012/07/10/auto-vectorizer-in-visual-studio-11-cookbook.aspx

저작자 표시
신고
by 흥배 2013.04.07 15:47

C++11의 새로운 기능 중 'SCARY iterator' 라는 것이 있습니다. 단순히 한국말로 바꾸면 '무서운 반복자' 라고 할 수 있는데 사실 무섭지는 않습니다.^^;

 

SCARY iterator에 대해서 아주 간단하게 말하면 컨테이너를 선언할 때 템플릿에 들어가는 데이터 타입만 같다면 비교 함수나 메모리 할당자가 서로 달라도 같은 반복자를 사용할 수 있게 해줍니다(만약 메모리 할당자의 차이에 의해서 이상한 결과가 나올 수도 있어서 무서운 이라는 단어를 사용하지 않았나 생각합니다);

 

SCARY iterator가 구현 되기 전에는 아래와 같이 데이터 타입은 같으나 비교 함수가 서로 다른


std::set< int, std::less<int> > map1;

std::set< int, std::greater<int> > map2;


에서 map1 map2의 반복자는 서로 같지 않습니다. 분명히 int라는 같은 타입을 사용해서 같은 반복자를 사용해도 문제 없

을 거라고 생각되지만 비교 함수가 서로 틀려서 반복자는 서로 다른 타입으로 취급합니다. 그래서 아래의 코드는 컴파일 에러가 발생합니다.

 

std::set<int, std::less<int>>::iterator Iter = b.begin();

 

그러나 C++11SCARY iterator가 구현된 VC11에서는 위의 코드는 전혀 에러가 발생하지 않습니다.

 

VC11처럼 SCARY iterator가 구현된 컴파일러에서는 컨테이너의 데이터 타입만 같다면 같은 반복자를 사용할 수 있게 되었습니다.

 

 


 

참고 : http://blogs.wankuma.com/episteme/archive/2013/01/11/311581.aspx

 

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

VC11의 프로젝트 속성 중에 'SDL 검사' 라는 것이 있습니다. 이 속성을 사용으로 하면 보안상 위험한 함수를 사용하면 컴파일 에러를 발생시킵니다.

 

 

 

보안상 위험한 함수 중 가장 대표적인 것이 아래와 같은 문자열 복사입니다.

strcpy( szSource, TEST_TEXT );


보통은 위 함수를 사용하면 컴파일 중 경고로 다른 버전을 사용하라고 출력하는데 SDL 검사를 활성화 하면 에러로 처리합니다.


 

자꾸 위험한 함수를 사용하는 사람이 있으면 이 기능을 사용하여 막을 수 있을 것 같네요 

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

VS 2005를 사용하여 만든 프로젝트를 VS2012로 마이그레이션 하는 도중 템플릿 사용 부분에서 에러가 발생했습니다.

 

제가 겪은 에러가 이미 다른 사람들도 경험을 했고 MSConnect 사이트에 해결 책이 있어서 공유합니다.

 

struct default_policy

{

    template<class T>

    struct apply

    {

        typedef T type;

    };

};

 

template<class Policy=default_policy>

struct Def

{

    typedef Policy policy;

    typedef Def<policy> thisClass;

 

    typedef typename policy::apply<thisClass>::type type; // syntax error '<'

};

 

void main()

{

}

위 코드에서 typedef typename policy::apply<thisClass>::type type; 부분에서 에러가 발생합니다.

 

이것은 VC11의 문제가 아니고 위 코드가 표준을 지키지 않아서 발생한 문제입니다.

 

해결책은 아래와 같습니다(간단합니다)

struct default_policy

{

    template<class T>

    struct apply

    {

        typedef T type;

    };

};

template<class Policy=default_policy>

struct Def

{

    typedef Policy policy;

    typedef Def<policy> thisClass;

    typedef typename policy::template apply<thisClass>::type type;

};

 

 

typedef typename policy::apply<thisClass>::type type;

이것을 아래와 같이 바꾸면 됩니다.

typedef typename policy::template apply<thisClass>::type type;

 

 

출처 : http://connect.microsoft.com/VisualStudio/feedback/details/745629/typename-template-bug-in-c-compiler-in-visual-studio-2012-rc

 

 

저작자 표시
신고
by 흥배 2012.11.27 08:00
| 1 2 |

티스토리 툴바