C++11 멀티스레드std::mutex편(4).

13937 단어
Mutex
C++ 11에서 Mutex와 관련된 클래스 (자물쇠 형식 포함) 와 함수는 모두 헤더 파일에 성명되어 있기 때문에 std::mutex를 사용하려면 헤더 파일을 포함해야 합니다.
Mutex:
std::mutex 기본 호환 클래스.
std::recursive_mutex 컴포지팅 클래스.
std::time_mutex 정시 호출 클래스.
std::recursive_timed_mutex 정시 귀속 호환 클래스.
Locks:
std::lock_guard는 상호 배척 클래스에 자물쇠를 채운다.
std::unique_lock에서 더 좋은 잠금 해제를 제공합니다.
Other types:
std::onece_flag
std::adopt_lock_t
std::defer_lock_t
std::try_to_lock_t
 
Functions:
std::try_lock
std::lock
std::call_once
 
std::mutex
std::mutex는 C++11의 가장 기본적인 상호 배척량입니다. std::mutex 대상은 독점 소유권의 특성을 제공합니다. 즉, std::mutex 대상에 대한 귀속적인 잠금이 지원되지 않으며 std::recursivelock는 호환 배율 대상에 자물쇠를 채울 수 있습니다.
구조 함수:
constexpr mutex() noexcept;
mutex (const mutex&) = delete;

복사 구조 함수는 삭제되었기 때문에 복사와 std::move 복사를 할 수 없습니다.초기 상태의mutex 대상은 unlocked에 있습니다.
 
구성원 함수:
void lock();

어떤 매개 변수도 받아들이지 않고, 어떤 종류도 되돌려주지 않습니다.
그 중요한 역할은 현재 스레드가 메모리에 있는 데이터에 접근하고 있을 때 다른 스레드는 접근할 권리가 없다는 것이다.잠금 해제(unlock)를 기다리지 않는 한
현재 라인에서 이 함수를 호출하면 상호 배척량을 잠글 수 있습니다.하지만 세 가지 상황이 발생한다.
(1). 만약 이 호출량이 현재 잠겨 있지 않다면, 호출 루틴은 unlock을 호출하기 전까지 이 루틴을 잠글 것입니다.
(2). 현재 배척량이 다른 라인에 잠겨 있으면, 현재 호출된 라인이 막힙니다.
(3). 현재 호출된 라인에 현재 배율이 잠겨 있으면, 사라짐 (deadlock) 이 발생합니다.
 // mutex::lock/unlock
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex
std::mutex mtx;           // mutex for critical section
//     ,                       . 
void print_thread_id (int id) {
  // critical section (exclusive access to std::cout signaled by locking mtx):
  mtx.lock();
  std::cout << "thread #" << id << '
';   mtx.unlock(); }
int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(print_thread_id,i+1);
  for (auto& th : threads) th.join();
  return 0;
}

 
구성원 함수:
bool try_lock();

try_ck (), 호환량을 잠그려고 시도합니다. 현재 라인이 이 호환량을 잠그는 데 성공하면true로 되돌아갑니다. 그렇지 않으면false로 되돌아갑니다. 현재 라인이 사용하는 호환량이 잠겼다면 이 함수를 다시 호출해도 현재 라인이 막히지 않습니다.
하지만 세 가지 상황이 발생한다.
1, 현재 배척량이 다른 라인에 점유되지 않으면, 이 라인은 배척량을 잠그고, 현재 라인이 unlock을 호출하여 배척량을 해제할 때까지 잠그십시오.true로 돌아가기
2, 현재 배척량이 다른 라인에 잠겨 있으면, 현재 호출된 라인은false로 되돌아오고, 현재 라인은 막히지 않습니다.
3, 현재 호출된 라인에 의해 현재 배척량이 잠기면 사라짐 (deadlock) 이 발생합니다.
 // mutex::try_lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex
volatile int counter (0); // non-atomic counter
std::mutex mtx;           // locks access to counter
void attempt_10k_increases (const int& id_) {
  for (int i=0; i<10000; ++i) {
    if (mtx.try_lock()) {   // only increase if currently not locked:
      ++counter;            //           mtx       counter; 
      if(i == 0){
       std::cout<<"thread----id: "<<id_<<'
';         }       mtx.unlock();     }   }      std::cout<<"thrad#: "<<id_<<'
'; }
int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(attempt_10k_increases, i);
  for (auto& th : threads) th.join();
  std::cout << counter << " successful increases of the counter.
";
  return 0;
}

구성원 함수:
void unlock();

현재 라인의 상호 배척량에 대한 소유권을 방출합니다.
 
구성원 함수:
native_handle_type native_handle();

이 함수는 비교적 특수하다.구체적으로 무슨 용도로...몰라.
이 함수는mutex에서 성명만 제시한 것 같은데, 우리가 스스로 실현할 수 있습니까?
 
 
std::recursive_mutex
std::recursive_mutex는 std::mutex와 마찬가지로 잠길 수 있는 대상이기도 하지만 std::mutex와 달리 std::recursivemutex는 같은 라인이 서로 밀어내는 양을 여러 번 잠그는 것을 허용합니다. (즉, 귀속 잠금) 서로 밀어내는 대상에 대한 다중 소유권을 얻을 수 있습니다. std::recursivemutex가 상호 배척량을 방출할 때 이 자물쇠의 깊이와 같은 횟수의 unlock () 을 호출해야 합니다. lock () 횟수와 unlock () 횟수가 같은 것으로 이해할 수 있습니다. 이외에 std::recursivemutex의 특성은 std::mutex와 대체로 같다.
 
std::timed_mutex
std::timed_mutex는 std::mutex에 비해 두 개의 구성원 함수가 더 많음:trylock_for() 및 trylock_until()
template <class Rep, class Period>
  bool try_lock_for (const chrono::duration<Rep,Period>& rel_time);

try_lock_for 함수는 시간 범위를 받아들인다. 이 시간 범위 내에서 라인이 자물쇠를 얻지 못하면 막힌다. (Blocking) 이 기간에 다른 라인이 자물쇠를 풀면, 이 라인은 서로 밀어내는 자물쇠를 얻고true로 되돌아갈 수 있으며, 시간 초과 (즉 지정된 시간 내에 자물쇠를 얻지 못하면) 하면false로 되돌아온다.
 #include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
std::timed_mutex mtx;
void fireworks(const int& id_)
{
 //  200                        .
 //    200            (             ). 
 //      false      '-'. 
  
 while( !mtx.try_lock_for(std::chrono::milliseconds(200)) ){
  std::cout<<"-";
 }
 
 std::this_thread::sleep_for(std::chrono::seconds(1));
 std::cout<<"*
";    mtx.unlock(); }
int main()
{
 std::thread threads[10];
 for(int i=0; i<10; ++i){
  threads[i] = std::thread(fireworks, i);
 }
 
 for(int j=0; j<10; ++j){
  threads[j].join();
 }
 
 return 0;
}

 try_lock_until 함수는 하나의 시간점을 매개 변수로 받아들인다. 지정된 시간점이 오기 전에 라인이 자물쇠를 얻지 못하면 막힌다. 이 기간에 다른 라인이 자물쇠를 풀면, 이 라인은 서로 밀어내는 자물쇠를 얻을 수 있고, 시간이 초과되면 (즉 지정된 시간 안에 자물쇠를 얻지 못하면) false로 되돌아온다.
 
std::lock_guard
template <class Mutex> class lock_guard;

 lock_guard는 그(lock guard)의 생명주기 내 총 배척량이 항상 잠겨 있음을 보증한다.lockguard의 생명주기가 끝난 후 상호 배척량은 잠금 해제됩니다.설령 현재 라인이 실행되는 과정에서throw가 이상하게 lock 을 통과했다 하더라도guard 관리 대상도 상호 배척량을 해제할 수 있습니다.
ock_guard 대상은 보통 어떤 자물쇠 (Lock) 대상을 관리하는 데 사용되기 때문에, Mutex RAII와 관련되어, 상호 배율에 대한 라인의 자물쇠를 편리하게 잠글 수 있습니다. 즉, 어떤 lockguard 대상의 성명 주기 내에 그가 관리하는 자물쇠 대상은 계속 자물쇠 상태를 유지할 것이다.lockguard의 생명주기가 끝난 후에 관리하는 잠금 대상은 잠금 해제됩니다. (주:shared ptr 등 스마트 포인터가 동적 분배된 메모리 자원을 관리합니다.)
템플릿 매개 변수 Mutex는 상호 배율 유형을 나타냅니다. 예를 들어 std::mutex 유형은 기본적인BasicLockable 유형이어야 합니다. 표준 라이브러리에서 몇 가지 기본적인BasicLockable 유형을 정의합니다. 각각std::mutex,std::recursivemutex, std::timed_mutex,std::recursive_timed_mutex, (주의해야 할 것은 BasicLockable 형식의 상호 배척량을 충족시키려면 lock과 unlock 작업만 충족시키면 됩니다.)
 
구조 함수:
explicit lock_guard (mutex_type& m);
lock_guard (mutex_type& m, adopt_lock_t tag); //     lock_guard unique_lock   .
lock_guard (const lock_guard&) = delete;

(1): 관리 mutextype 형식 대상 m, 구성할 때 m에 잠금 (lock).만약 m이 이미 다른 라인에 잠겼다면, 현재 라인을 먼저 막으십시오.
(2): 관리 mutextyoe 유형 대상 m, 1과 다른 것은 m 대상이 현재 라인에 잠겼습니다.
(3): 복사 구조는 delete로 되어 있기 때문에lockguard는 복사 구조와 이동 구조를 사용할 수 없습니다.(여기서 특히 지적해야 할 것은 우리가 이 유형의 구조 함수 컴파일러를 정의하면 더 이상 우리를 합성하지 않기 때문에 delete에서copy구조 함수만 삭제하면 모브구조 함수도 삭제되는 것과 같다)
 #include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void print_id(const int& id_)
{
 mtx.lock(); //   lock_guard mtx      lock   . 
 std::lock_guard<std::mutex> lg(mtx, std::adopt_lock); //  lock_guard  mtx    。 lg             .
 //          std::adopt_lock,               . 
 std::cout<<"thread#: "<<id_<<'
'; }
int main()
{
 std::thread threads[10];
 for(int i=0; i<10; ++i){
  threads[i] = std::thread(print_id, i);
 }
 
 for(int i=0; i<10; ++i){
  threads[i].join();
 }
 
 return 0;
}

 
 std::unique_lock
unique_lock는 더 좋은 자물쇠와 잠금 해제 제어를 제공할 수 있습니다.와lockguard의 차이점은 유니크lock는 mutex 대상을 관리할 때 절대적인 독립권을 가지고 있으며 두 개의 unique 가 존재할 수 없습니다lock 대상이mutex를 동시에 관리합니다.
 
주의해야 할 것은, 유니크lock 대상 역시 Mutex 대상의 생명주기 관리를 책임지지 않습니다.uniquelock 대상은 Mutex 대상의 잠금장치와 잠금 해제 작업을 간소화할 뿐, 상호 배율에 대한 연결을 편리하게 할 수 있습니다. 즉, 어떤 uniquelock 대상의 성명 주기 내에 관리하는 자물쇠 대상은 계속 잠금 상태를 유지합니다.유니크lock의 생명주기가 끝나면 관리하는 자물쇠 대상이 잠금 해제된다는 점과lockguard는 유사하지만 uniquelock은 프로그래머에게 더 많은 자유를 제공합니다. 다음 내용에서 유니크 를 소개해 드리겠습니다.lock의 사용법.
또한, lock 와guard와 마찬가지로 템플릿 매개 변수 Mutex는 상호 배율 유형을 대표한다. 예를 들어 std::mutex 유형, 이것은 기본적인BasicLockable 유형이어야 한다. 표준 라이브러리에서 몇 가지 기본적인BasicLockable 유형을 정의한다. 각각std::mutex,std::recursivemutex, std::timed_mutex,std::recursive_timed_mutex(이상 네 가지 유형은 모두 이전 블로그에 소개됨) 및 std::uniquelock.주:BasicLockable 유형의 대상은 두 가지 조작을 만족시키기만 하면 됩니다.lock과 unlock, 그리고 Lockable 유형도 있습니다.BasicLockable 유형에 try 를 추가하였습니다.lock 작업, 따라서 Lockable에 맞는 대상은 세 가지 동작을 지원해야 합니다: lock, unlock,trylock;마지막으로 TimedLockable 대상이 하나 더 있는데, Lockable 유형을 토대로trylock_for 및 trylock_until 두 가지 동작입니다. 따라서 TimedLockable를 만족시키는 대상은 다섯 가지 동작을 지원합니다: lock, unlock,trylock, try_lock_for, try_lock_until).
unique_lock() noexcept;
explicit unique_lock (mutex_type& m); //     operator=,mutex        delete .
unique_lock (mutex_type& m, try_to_lock_t tag);
unique_lock (mutex_type& m, defer_lock_t tag) noexcept
unique_lock (mutex_type& m, adopt_lock_t tag);
template <class Rep, class Period>
unique_lock (mutex_type& m, const chrono::duration<Rep,Period>& rel_time);
template <class Clock, class Duration>
unique_lock (mutex_type& m, const chrono::time_point<Clock,Duration>& abs_time);
unique_lock (const unique_lock&) = delete;
unique_lock (unique_lock&& x);

(1) 기본 구조 함수:
유니크 만들기lock 대상은 어떤 대상도 관리하지 않습니다.
(2):
새로 만든 유니크lock 대상 관리 시도 (lock)m, 만약 m가 다른 라인에 잠겼다면 현재 라인을 막고 다른 라인이 m를 잠그기를 기다린 다음 관리를 인수합니다.
(3):
새 uniquelock 대상이 m의try 를 호출하려고 시도합니다lock()가 m에 자물쇠를 채웁니다.현재 스레드가 잠기지 않으면 막히지 않습니다. (Blocking) 다른 스레드가 m을 잠그기를 기다리지 않고 다시 인수해서 아래로 실행합니다.
현재 라인 내의 다른 내용을 이어서 실행합니다.
(4):
새 uniquelock 대상이 m을 잠그지 않았습니다. m는 다른 라인에 잠겼을 것입니다.
(5):
유니크 만들기lock 대상이 가리키고 m이 현재 라인에 잠겼습니다,uniquelock가 m의 소유권을 인수합니다.
(6):
새 uniquelock 대상, m의try 호출 시도lock_for(rel time), m을 잠급니다.구체적인 표현은trylokc_for. rel_시간은 한때다.
(7):
unique_lock 대상이 m의try 를 호출하려고 시도합니다lock_until(abs time) m.
(8):
unique_lock는 구조를 이동할 수 있습니다.
 // unique_lock constructor example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::lock, std::unique_lock
                          // std::adopt_lock, std::defer_lock
std::mutex foo,bar;
void task_a () {
  std::lock (foo,bar);         // simultaneous lock (prevents deadlock)
  std::unique_lock<std::mutex> lck1 (foo,std::adopt_lock);
  std::unique_lock<std::mutex> lck2 (bar,std::adopt_lock);
  std::cout << "task a
";   // (unlocked automatically on destruction of lck1 and lck2) }
void task_b () {
  std::unique_lock<std::mutex> lck1, lck2;
  lck1 = std::unique_lock<std::mutex>(bar,std::defer_lock); //  unique_lock       . 
  lck2 = std::unique_lock<std::mutex>(foo,std::defer_lock);
  std::lock (lck1,lck2);       // simultaneous lock (prevents deadlock) //       std::lock       (deadlock). 
  std::cout << "task b
";   // (unlocked automatically on destruction of lck1 and lck2) }
void task_c()
{
 foo.lock();
 std::cout<<"task c
";  foo.unlock(); }
int main ()
{
  std::thread th1 (task_a);
  std::thread th2 (task_b);
  std::thread th3 (task_c);
  
  th1.join();
  th2.join();
  th3.join();
  
  return 0;
}

 std::unique_lock 구성원 함수:
unique_lock& operator= (unique_lock&& x) noexcept;
unique_lock& operator= (const unique_lock&) = delete;

unique_lock는 이동 값을 지원하지만 복사가 금지되어 있습니다.
std::unique_lock의 주요 구성원 함수:
std::unique_lock::lock()
잠금 작업을 수행하여 관리되는 Mutex 객체의 lock 함수를 호출합니다.만약 Mutex 대상의 lock 함수를 호출할 때 이 Mutex 대상이 다른 라인에 잠겼다면, 현재 라인은 잠금이 될 때까지 막힐 것입니다.
이 함수가 되돌아올 때 현재 uniquelock 객체는 해당 객체가 관리하는 Mutex 객체의 잠금을 갖습니다.자물쇠 잠금 작업이 실패하면 시스템 던지기error 예외.
 
std::unique_lock::try_lock()
관리되는 mutex 대상의try 호출 시도lock에 자물쇠를 채우다.자물쇠를 잠그지 못하면false를 되돌려줍니다.true가 반환되었습니다.
하면, 만약, 만약...lock 대상 관리의mutex가 현재 라인에 잠겼습니다. 이 함수를 호출하면 (throw)시스템error 예외.또는 mutex 대상을 관리하지 않은 uniquelock에서 이 함수를 호출하면 이상이 발생합니다.
 // unique_lock::try_lock example
#include <iostream>       // std::cout
#include <vector>         // std::vector
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::unique_lock, std::defer_lock
std::mutex mtx;           // mutex for critical section
void print_star () {
  std::unique_lock<std::mutex> lck(mtx,std::defer_lock); //              std::defer_lock.
  //std::deder_lock    :  unique_lock     mtx,         mtx  . 
  // print '*' if successfully locked, 'x' otherwise: 
  if (lck.try_lock())
    std::cout << '*';
  else                    
    std::cout << 'x';
}
int main ()
{
  std::vector<std::thread> threads;
  for (int i=0; i<500; ++i)
    threads.emplace_back(print_star);
  for (auto& x: threads) x.join();
  return 0;
}

좋은 웹페이지 즐겨찾기