728x90

atomic 뜻 : https://grandstayner.tistory.com/entry/Automic-Operation%EC%9B%90%EC%9E%90%EC%A0%81-%EC%97%B0%EC%82%B0

 

용어 - Automic Operation(원자적 연산)

간단하게 말하면 중단이 불가능한 연산을 의미한다. 문제는 이를 얼마나 이해하는 것인데 자신이 작성한 코드가 원자적 연산이 아닐 수도 있다는 사실을 알아야 한다. 01: public class Example { 02:  i

grandstayner.tistory.com

 

신입 프로그래머들의 경우에는 위의 사진에서 7번 라인의 a++이 1개의 명령으로 실행이 된다고 생각할 수 있다.

하지만, 이것을 디스어셈블리 해보면 다음과 같이 여러개의 명령어로 실행이되는 것을 알 수 있다.

눈에 보이는 소스코드처럼 1번에 실행이 되는 것이 아니라면 Multi Thread 환경에서는 오동작을 일으킬 확률이 있기 때문에 C++ 11에서는 std::atomic 이라는 구조체를 제공하고 있다.

쉽게 설명하면 std::atomic의 템플릿 인자로 원자적으로 만들고 싶은 타입을 전달하면 그 변수는 기본적으로 원자적인 변수로 동작하게 바뀐다.

아래와 같이 atomic_int를 사용했을 때 디스어셈블리를 확인해보자.

위의 사진과 같이 lock xadd 라는 명령어가 보인다. 즉 lock을 걸어서 a++작업 자체를 원자적으로 동작하는 것을 보장해준다는 것이다.

 

사용법은 아래와 같이 단순하다.

#include <iostream>

int main()
{
    std::atomic<bool> boolean = false;
    boolean = true;
    boolean.store(true);
}

 

종종 사용되는 함수도 봐둘 필요는 있다.

#include <iostream>

std::atomic<bool> gStoppedThread = false;

void LoopThread()
{
    while (gStoppedThread == false)
    {

    }

    gStoppedThread = true;
}

만약 위와 같이 while(gStoppedThread = false)과 gStoppedThread = true 자체가 한번에 처리되어야 하는 상황이 있을 수 있다. 그럴 때는 atomic::compare_exchange_strong() 를 사용하면 된다.

변수 expected(기대값), desired(atomic 변수 값이 expected값과 같다면 이 값으로 적용) 가 추가되었다.

compare_exchange_strong()의 내부 동작은 의사 코드와 거의 동일할 것으로 예상된다.

즉 실제 코드와 같이 변경해주면 저 동작은 원자적으로 동작하는 것을 보장할 수 있다.

ref : https://aerocode.net/117

 

Compare And Exchange(CAS) 명령어

Compare And Exchange (CAS) Compare And Exchange (CAE) 명령어는 락을 사용하지 않는 기본 원자함수이며(CPU 레벨에서 지원한다.), 비교와 교환을 원자성을 가지고 수행한다. Compare And Swap (CAS) 라는 이름..

aerocode.net

 

728x90

'Basic Programming > C++ 11' 카테고리의 다른 글

C++11 - Mordern C++ 프로그래머를 위한 C++ 11/14 핵심  (0) 2018.10.17
c++11 - constexpr  (0) 2018.08.22
C++11 - final, override 키워드  (0) 2017.09.18
C++11 - std::thread  (0) 2016.12.02
C++11 - "default" and "delete" keyword  (0) 2016.11.29
728x90
728x90

'Basic Programming > C++ 11' 카테고리의 다른 글

c++11 - atomic  (2) 2022.10.13
c++11 - constexpr  (0) 2018.08.22
C++11 - final, override 키워드  (0) 2017.09.18
C++11 - std::thread  (0) 2016.12.02
C++11 - "default" and "delete" keyword  (0) 2016.11.29
728x90
1. constexpr


기존의 const 보다 훨씬 더 상수성에 충실하며, 컴파일 타임 함수를 이용한 성능 향상 등 충분히 깊게 이해할만한 가치가 있는 녀석이라 할 수 있으니, 확실히 이해하고 활용할 수 있는 것이 중요하다.

C++11부터 지원되는 한정자 constexpr는 일반화된 상수 표현식(Generalized constant expression)을 사용할 수 있게 해주며, 일반화된 상수 표현식을 통해 변수나 함수, 생성자 함수에 대하여 컴파일 타임에 평가될 수 있도록 처리해 줄 수 있다.
(C++17부터는 람다 함수에도 constexpr 키워드 사용이 가능하다)

constexpr 변수 또는 함수의 반환값은 반드시 LiteralType이어야 하며, LiteralType은 컴파일 타임에 해당 레이아웃이 결정될 수 있는 타입을 의미한다. 다음은 리터럴 타입들의 유형이다.
  • void
  • 스칼라 값
  • 참조
  • void, 스칼라 값, 참조의 배열
  • trivial 소멸자 및 이동 또는 복사 생성자가 아닌 constexpr 생성자를 포함하는 클래스. 또한 해당 비정적 데이터 멤버 및 기본 클래스가 모두 리터럴 타입이고 volatile이 아니어야 함

코드 작업 중 해당 타입이 리터럴 타입인지 확인하고 싶을 땐, std::is_literal_type을 사용하면 된다.


1) 변수에서의 사용

const와 constexpr의 주요 차이점은 const 변수의 초기화를 런타임까지 지연시킬 수 있는 반면, constexpr 변수는 반드시 컴파일 타임에 초기화가 되어 있어야 한다.
초기화가 안 되었거나, 상수가 아닌 값으로 초기화 시도시 컴파일이 되지 않는다.

  1. constexpr float x = 42.f;    // OK
  2. constexpr float y { 108.f }; // OK
  3. constexpr int i;             // error C2737: 'i': 'constexpr' 개체를 초기화해야 합니다.
  4. int j = 0;
  5. constexpr int k = j + 1;     // error C2131 : 식이 상수로 계산되지 않았습니다.

변수에 constexpr 사용시 const 한정자를 암시한다.


2) 함수에서의 사용

constexpr을 함수 반환값에 사용할 때는 다음의 제약들이 따른다.
  • 가상으로 재정의된 함수가 아니어야 한다.
  • 반환값의 타입은 반드시 LiteralType이어야 한다.

함수에 constexpr을 붙일 경우 inline을 암시한다.
즉, 컴파일 타임에 평가하기 때문이며, inline 함수들과 같이 컴파일된다.

C++11에서는 함수 본문에 지역변수를 둘 수 없고, 하나의 반환 표현식만이 와야 하는 제약이 있었으나, C++14부터는 이러한 제약이 사라졌다.

  1. // C++11/14 모두 가능
  2. constexpr int factorial(int n)
  3. {
  4.     // 지역 변수 없음
  5.     // 하나의 반환문
  6.     return n <= 1 ? 1 : (* factorial(- 1));
  7. }
  8.  
  9. // C++11에서는 컴파일 에러 발생
  10. // C++14부터 가능
  11. constexpr int factorial(int n)
  12. {
  13.     // 지역 변수
  14.     int result = 0;
  15.  
  16.     // 여러 개의 반환문
  17.     if (<= 1)
  18.         result = 1;
  19.     else
  20.         result = n * factorial(- 1);
  21.  
  22.     return result;
  23. }

constexpr 함수는 컴파일러에게 가능하다면, 상수시간에 컴파일해 달라고 요청하는 것이지만 상황에 따라 컴파일 타임에 미리 실행될 수도 있고, 그렇지 못하고 런타임에 실행될 수도 있다.
(마치 inline 키워드가 그러하듯이 말이다)

constexpr의 함수 인자들이 constexpr 규칙에 부합하지 못하는 경우엔 컴파일 타임에 실행되지 못하고 런타임에 실행된다.
런타임 실행 여부는 여러 가지 방식으로 검증해 볼 수 있다.
  • breakpoint 걸어서 중단되는지 확인
  • 아래 예제의 constN 같은 테스팅 템플릿 작성
  • 정수일 경우 배열의 dimension으로 작성

  1. // 템플릿 인자 N이 컴파일 타임 상수인지 테스트하기 위한 템플릿 구조체
  2. template<int n>
  3. struct constN
  4. {
  5.     constN() { std::cout << N << '\n'; }
  6. };

  7. constexpr int factorial(int n)
  8. {
  9.     return n <= 1 ? 1 : (* factorial(- 1));
  10. }
  11.  
  12. int main()
  13. {
  14.     // 4는 리터럴 타입이므로 상수 타임에 컴파일 성공
  15.     constN<factorial(4)> out1;
  16.  
  17.     // ab는 4의 값을 가지지만, 리터럴 타입이 아니므로 컴파일 에러 발생
  18.     // error C2975: 'N': 'constN'의 템플릿 인수가 잘못되었습니다. 컴파일 타임 상수 식이 필요합니다.
  19.     int ab = 4;
  20.     constN<factorial(ab)> out2;
  21.  
  22.     // 리터럴 타입이 아니므로, 이 함수는 런타임에 실행된다.
  23.     // 하지만 정상 동작한다.
  24.     int cd = factorial(ab);
  25.  
  26.     return 0;
  27. }

25라인에서 factorial 함수의 인자가 constexpr로 평가될 수 없기에, 함수이지만 런타임에 실행되는 것을 확인할 수 있다. 
이처럼 constexpr 함수는 인자가 constexpr에 부합한지에 따라, 컴파일 타임 또는 런타임에 실행되기에 범용적으로 사용되는 함수이고, 실행의 복잡도가 낮지 않다면, 가급적 constexpr 키워드를 붙이는 것도 괜찮은 습관이 되지 않을까 생각한다.

위의 예제에서는 함수가 어느 시점에 실행되는지 여부를 살펴보았지만, 무작정 constexpr 키워드를 붙일 수 있는 것도 아니다.
함수가 절대 상수표현식으로써 평가받지 못하는 문맥을 가지는 경우엔 컴파일 에러가 발생한다.

  1. constexpr int rand_short()
  2. {
  3.     // 절대 상수화가 될 수 없는 문맥
  4.     // error C3615: constexpr 함수 'randshort'의 결과가 상수 식이면 안 됩니다.
  5.     return rand() % 65535;
  6. }

컴파일 에러니까 함수 작성 후 바로 확인이 가능하기에 심각한 오류를 사전에 만나는 일은 없을 것이다.


3) 생성자 함수에서의 사용

LiteralType 클래스를 생성할 때 constexpr 생성자를 사용할 수 있다.
이 때의 제약은 다음과 같다.
  • 모든 생성자 함수의 매개변수들 역시 LiteralType들이어야 한다.
  • 어떤 클래스로부터 상속받지 않아야 한다

constexpr이 적용된 함수의 매개변수는 반드시 LiteralType이어야 한다고 했다.
이를 만족시키기 위해 constexpr 생성자 함수를 이용, LiteralType 클래스를 활용하는 예제를 살펴보도록 하자.

  1. // 아래 CountLowercast 함수의 인자로 사용하기 위한 LiteralType class
  2. class ConstString
  3. {
  4. private:
  5.     const char* p = null;
  6.     std::size_t sz = 0;
  7.  
  8. public:
  9.     template<std::size_t N>
  10.     constexpr ConstString(const char(&a)[N])
  11.     : p(a), sz(- 1)
  12.     {
  13.     }
  14.  
  15. public:
  16.     constexpr char operator[](std::size_t n) const
  17.     {
  18.         return n < sz ? p[n] : throw std::out_of_range("");
  19.     }
  20.  
  21. public:
  22.     constexpr std::size_t size() const { return sz; }
  23. };
  24.  
  25. // 소문자가 몇개인지 수를 세는 함수
  26. constexpr std::size_t CountLowercase(ConstString s, std::size_t n = 0, std::size_t c = 0)
  27. {
  28.     return n == s.size() ?
  29.                     c :
  30.                     s[n] >= 'a' && s[n] <= 'z' ?
  31.                         CountLowercase(s, n+1, c+1) :
  32.                         CountLowercase(s, n+1, c);
  33. }
  34.  
  35. // 컴파일타임 상수를 테스트해 보기 위한 템플릿
  36. template<int n>
  37. struct ConstN
  38. {
  39.     ConstN()
  40.     {
  41.         std::cout << n << '\n';
  42.     }
  43. };
  44.  
  45. int main()
  46. {
  47.     std::cout << "Number of lowercase letters in \"Hello, world!\" is ";
  48.  
  49.     // "Hello, world!"가 ConstString으로 암시적 형반환되어, CountLowercase 함수의 인자로 넘어감.
  50.     // CountLowercase는 컴파일 타임에 평가되었고, 그 결과를 가지고 ConstN 역시 컴파일 타임에 결정됨.
  51.     ConstN<CountLowercase("Hello, world!")> out;
  52.  
  53.     return 0;
  54. }


2. 템플릿 메타 프로그래밍 vs constexpr

constexpr은 컴파일 타임에 평가되기 때문에 템플릿 메타 프로그래밍(템플릿 함수가 인스턴스화될 때 값 계산)과 비교될 수 있다.
예를 들어, 똑같이 배열의 크기나 enum 열거형의 값과 같은 곳에서 상수로써 사용이 가능하다.

  1. // 템플릿 함수 방식의 Factorial
  2. template <int N>
  3. struct Template_Factorial
  4. {
  5.     enum { value = N * Template_Factorial<- 1>::value; }
  6. };
  7.  
  8. template <>
  9. struct Template_Factorial<0>
  10. {
  11.     enum { value = 1; }
  12. }
  13.  
  14. // constexpr 방식의 Factorial
  15. constexpr int Constexpr_Factorial(int n)
  16. {
  17.     return n <= 0 ? 1 : n  * Constexpr_Factorial(- 1);
  18. }
  19.  
  20. // constexpr 함수의 결과를 enum의 값으로 사용 가능
  21. enum FACTORIAL
  22. {
  23.     first  = Constexpr_Factorial(1),
  24.     second = Constexpr_Factorial(2),
  25.     third  = Constexpr_Factorial(3),
  26. };

위 예제에서 보듯이, 기존의 TMP에서 0과 같은 특수값을 사용하기 위해 템플릿 특수화를 했던 것에 비해, constexpr 함수는 훨씬 더 직관적인 방법을 제공할 수 있다.

위 예제에 피보나치까지 살짝 추가해, 조금 더 비교해 보면 기존 코드를 죄다 다시 작성하고 싶어질 것이다.

  1. template<unsigned n>
  2. struct Fibonacci
  3. {
  4.     static const unsigned value = Fibonacci<- 1>::value + Fibonacci<- 2>::value;
  5. };
  6.  
  7. template<>
  8. struct Fibonacci<0>
  9. {
  10.     static const unsigned value = 0;
  11. };
  12.  
  13. template<>
  14. struct Fibonacci<1>
  15. {
  16.     static const unsigned value = 1;
  17. };
  18.  
  19. int main()
  20. {
  21.     return Fibonacci<5>::value;  
  22. }
  23.  
  24. ///////////////////////////////////////////////////////////////////////////////////////////////

  25. constexpr unsigned fibonacci(const unsigned x)
  26. {
  27.     return x <= 1 ? 1 : fibonacci(- 1) + fibonacci(- 2);
  28. }
  29.  
  30. int main()
  31. {
  32.     return fibonacci(5);
  33. }


출처 : http://egloos.zum.com/sweeper/v/3147813

728x90

'Basic Programming > C++ 11' 카테고리의 다른 글

c++11 - atomic  (2) 2022.10.13
C++11 - Mordern C++ 프로그래머를 위한 C++ 11/14 핵심  (0) 2018.10.17
C++11 - final, override 키워드  (0) 2017.09.18
C++11 - std::thread  (0) 2016.12.02
C++11 - "default" and "delete" keyword  (0) 2016.11.29
728x90

C++ 에서는 명시적으로 클래스의 상속을 막는 방법이 없기 때문에 final 이라는 키워드가 추가되었다.


final 키워드

이 키워드는 클래스에 사용하면 클래스를 상속할 수 없고, 함수에 사용하면 함수를 상속할 수 없다.


아래 사진은 클래스에 final을 붙였을 때 이다.


아래 사진은 함수에 final을 붙였을 때 이다.



override 키워드

이 키워드는 클래스 함수의 오버라이드를 막을 때 사용된다.


아래 사진은 함수에 override를 붙였을 때 이다.



728x90
728x90

C++11에서 가장 큰 장점 중 하나는 std::thread 일 것이다.


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


std::thread를 사용하기 위해선 #include <thread>만 추가하면 된다. 다른 라이브러리는 추가할 필요가 없다.


기본적인 사용방법은 아래와 같다.


밑에 join() 함수를 사용하지 않으면, Thread보다 main()가 먼저 끝나기 때문에 에러가 발생하게 된다. 주의하도록 하자.


Thread에 매개 변수를 넘기기 위해서는 아래와 같이 하면된다.


Visual Studio 2012에서는 넘길 수 있는 매개변수의 개수가 5개였다. (어떤 사이트에서 4개라고 봤는데... Update가 다른지는 모르겠지만 나의 경우 5개인 것을 확인하였다.)


마지막으로 joinable() 함수로 이 std::thread 변수가 thread객체를 가지고 있는 것을 확인하고 종료할 수도 있다.


더 많은 기능이 있지만, 현재는 이정도만 사용할 것 같아서... ^^;;

728x90
728x90
들어가기에 앞서, 다음 링크는 default / delete에 대한 C++ 표준 문서이다.


C++ 클래스를 사용 하다보면, 컴파일러가 은근슬쩍 만들어내는 함수가 4가지가 있다.

  • 기본 생성자
  • 복사 생성자
  • 대입 연산자
  • 소멸자
이것들이 보통의 경우 별로 신경 쓸 일이 없는데, 다음과 같은 상황에선 손을 좀 써 줘야 한다.


1. 기본 생성자 요구 (default)

  1. class NonCopyable
  2. {
  3. private:
  4.     NonCopyable(const NonCopyable&);
  5. };
  6.  
  7. int main()
  8. {
  9.     // Compile error : C2512: 'NonCopyable' : 사용할 수 있는 적절한 기본 생성자가 없습니다.
  10.     NonCopyable nc;
  11. }

    기본 생성자는 기본 생성자가 아닌, 어떠한 생성자라도 존재할 경우 자동으로 생성되지 않는다.
    (이것이 복사 생성자가 되었던, 인자를 가지는 다른 형태의 생성자이든 말이다).

    따라서, 기본 생성자 형으로 객체를 생성하면, 사용할 수 있는 적절한 기본 생성자가 없다며 컴파일 에러가 발생한다.
    이 경우, 아래와 같이 기본 생성자를 직접 선언/정의해 주어야 컴파일 에러가 발생하지 않는다.

    1. class NonCopyable
    2. {
    3. public:
    4.     NonCopyable() {}
    5.  
    6. private:
    7.     NonCopyable(const NonCopyable&);
    8. };
    9.  
    10. int main()
    11. {
    12.     NonCopyable nc;
    13. }

    이 경우 C++11에서는 아래와 같이 default 생성자를 선언만으로 해결할 수 있다.

    1. class NonCopyable
    2. {
    3. public:
    4.     NonCopyable() = default;
    5.  
    6. private:
    7.     NonCopyable(const NonCopyable&);
    8. };
    9.  
    10. int main()
    11. {
    12.     NonCopyable nc;
    13. }

      위 예제에서는 NonCopyable() 생성자의 default 생성을 명시적으로 요구하였으므로, 기본 생성자가 생성된다.


      2. 복사, 대입 금지 클래스 (delete)

      특정 클래스에 대해선 복사도 대입도 막고 싶은 경우 기존 C++ 0x 까진 아래와 같이 작성하였다.

      1. class NonCopyable
      2. {
      3. public:
      4.     NonCopyable() {}
      5.     ~NonCopyable() {}
      6.  
      7. private:
      8.     NonCopyable(const NonCopyable&);
      9.     NonCopyable& operator = (const NonCopyable&);
      10. };

      위 방식은 우선 복사 생성자와 대입 연산자를 private으로 두어 외부에서 접근 에러가 발생하게 하는 방식이고,

      만약, 위 코드에서 복사 생성자와 대입 연산자의 접근 지정자를 public으로 두었다면,
      선언만 하고 정의를 하지 않아 링크 에러가 나게 하는 식으로 사용을 금지시키는 방식이었다.

      이를 C++11의 delete를 명시적으로 붙여주면, 이들에 대한 사용이 확실히 금지 된다.

      1. class NonCopyable
      2. {
      3. public:
      4.     NonCopyable() {}
      5.     ~NonCopyable() {}
      6.  
      7.     // 명시적으로 복사 생성자 / 대입 연산자가 disable 처리 되었음
      8.     NonCopyable(const NonCopyable&) = delete;
      9.     NonCopyable& operator = (const NonCopyable&) = delete;
      10. };

      음 여기까지만 봤을 땐, 뭐 기존에 비해 크게 이득이 뭔가 싶겠지만,
      delete의 경우 조금 더 요긴하게 사용할 수 있다.

      delete 키워드는 아무 함수에나 사용할 수 있기 때문에, 
      멤버 함수의 파라미터에 대해 암시적인 형변환이 일어나는 것을 사전에 방지할 수 있다.

      1. // int 타입에 대해선 받지 않겠음.
      2. struct NoInt
      3. {
      4.     void f(double i);
      5.     void f(int) = delete;
      6. };
      7.  
      8. // f의 인자로 double 형을 제외하고는 완전히 금지하겠다.
      9. // 즉, 컴파일러가 암시적으로 형 변환하는 것까지 완벽 차단.
      10. struct OnlyDouble
      11. {
      12.     void f(double d);
      13.     template<class T> void f(T) = delete;
      14. };

      지금까지 default / delete 키워드에 대해 알아 보았지만, 아쉽게도 Visual Studio2013부터만 지원된다.



      728x90

      'Basic Programming > C++ 11' 카테고리의 다른 글

      C++11 - final, override 키워드  (0) 2017.09.18
      C++11 - std::thread  (0) 2016.12.02
      C++11 - std::mutex (lock_guard, try_lock, try_lock_for, recursive_mutex)  (0) 2016.11.25
      C++11 - nullptr  (0) 2016.02.26
      C++11 - enum  (0) 2016.02.26
      728x90

      병렬 프로그래밍을 할 때 std::mutex를 이용하여 여러개의 Thread에서 사용하는 공유 자원을 서로 침범 없이 사용할 수 있다. 하지만 lock을 사용하여 mutx에 대한 소유권을 얻은 후 unlock을 하지 않으면 Dead Lock에 빠지게 된다.


      예를 들면


      std::mutex.lock();


      -----


      if (true)

      return true;


      -----


      std::mutex.unlock();



      위와 같은 코드에서는 lock을 걸고 중간에 return을 했기 때문에 unlock을 하지 않게 된다.


      이것은 코딩을 해보면 굉장히 실수하기 쉽다.


      이런 실수를 방지하기 위해 C++ 11에서는 lock_guard라는 것이 나왔다.


      사용 예 :


      #include <iostream>

      #include <thread>

      #include <mutex>


      typedef lock_guard<std::mutex> LockMutex;


      int g_nCount = 0;

      std::mutex g_mtxUpdate;


      [ Thread 1 ]

      {

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

      {

      LockMutex AutoLock(g_mtxUpdate);

      g_nCount++;

      }

      }


      [ Thread 2 ]

      {

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

      {

      LockMutex AutoLock(g_mtxUpdate);

      g_nCount++;

      }

      }


      이렇게 사용하면 된다.


      이것은 continue, return, 혹은 괄호의 끝을 만나게 되면 자동으로 unlock을 시켜준다.


      -------------------------------------------------------------------------------------------


      다음은 try_lock()이다.


      원래 lock()은 다른 스레드에서 lock()이 걸려있다면 unlock()이 될 때까지 기다리게 된다.


      하지만 try_lock()은 다른 스레드에서 lock()이 걸려있다면, false를 리턴하며 빠져나온다. 즉 다른 스레드에서 lock이 걸려 


      자원을 사용할 수 없을 때에는 다른 일을 하고 싶을 때 사용하면 된다.


      -------------------------------------------------------------------------------------------


      지정한 시간 동안만 락을 시도하게 할 수도 있다. 이것을 timed_mutex 라고 부른다.


      timed_mutex의 종류 중 하나인 try_lock_for()를 이용하여 지정된 시간동안 lock()을 시도하게 할 수 있다.


      이 함수는 특정 시간 동안만 시도하고 성공하지 못하면 try_lock 호출을 멈추고 빠져나오길 바랄 때 사용하면 된다.


      사용 예 :


      if (mutex.try_lock_for(std::chrono::seconds(10)))

      {

      ----


      mutex.unlock();

      }


      -------------------------------------------------------------------------------------------


      Mutex의 lock 멤버 함수의 설명을 보면 lock을 호출한 Thread에서 unlock을 호출하지 않고 다시 lock을 호출하면 알 수 없는 동작을 한다고 되어 있다.


      실제로 동작시켜보면 프로그램이 죽어버리는 현상을 겪을 수 있을 것이다.


      그럴 때에는 recursive_mutex를 사용하면 된다. 이 Mutex는 같은 Thread에서 lock을 건 후 바로 lock을 걸어도 괜찮다. 


      단, lock을 호출한 횟수만큼 unlock을 호출해야 한다.


      사용 예 :


      #include <iostream>

      #include <thread>

      #include <mutex>


      typedef lock_guard<std::recursive_mutex> RecursiveLockMutex;


      std::recursive_mutex g_mtx;


      [ CheckFunction ]

      {

      RecursiveLockMutex autoLock(g_mtx);


      ------

      }


      [ AddFunction ]

      {

      RecursiveLockMutex autoLock(g_mtx);


      CheckFunction();


      ----------

      }


      위의 예제 코드를 보면 알 수 있듯이 사용방법은 간단하다. std::mutex -> std::recursive_mutex로 바꾸기만 하면된다.


      -------------------------------------------------------------------------------------------





      ps. Windows에서 std::mutex는 내부적으로 Critical Section API를 이용한다.

      728x90

      'Basic Programming > C++ 11' 카테고리의 다른 글

      C++11 - std::thread  (0) 2016.12.02
      C++11 - "default" and "delete" keyword  (0) 2016.11.29
      C++11 - nullptr  (0) 2016.02.26
      C++11 - enum  (0) 2016.02.26
      C++11 - range based for  (0) 2016.02.25
      728x90

      C++03까지는 Null Pointer를 나타내기 위해 NULL 매크로나 상수 0을 사용했다. 그러나 NULL 매크로나 상수 0을 사용하여 함수에 인자로 넘기면 int형으로 추론되어버리는 문제가 발생하기도 한다.


      그리하여 nullptr이라는 키워드가 생겼다.


      사용 방법은 Null Pointer로 '0'이나 'NULL'을 사용하던 것을 'nullptr'로 바꾸기만 하면된다.

      char* p = nullptr;




      728x90

      + Recent posts