2016-09-07 1 views
14

컴파일러는 lock 문과 unlock 문 사이에서 수정되는 변수를 확인하고 mutex에 바인딩하여 독점적으로 액세스 할 수 있도록합니까?C++ std :: mutex는 어떻게 리소스에 바인딩됩니까?

mutex.lock() 현재 범위에서 볼 수있는 모든 리소스를 잠급니다?

+13

뮤텍스는 어떤 변수도 잠그지 않고 단지 그것을 획득하려고하는 스레드를 차단합니다.이것은 프로그램이'lock()'과'unlock()'호출 사이에서만 접근 할 경우 변수를 보호하는 효과가있다. 코드에서 뮤텍스'lock()'과'unlock()'의 존재는 또한 컴파일러가 보호 된 변수 * 액세스 *의 메모리 액세스를 다시 정렬하는 것을 방지합니다. –

+3

또한 스레드가 동일한 뮤텍스 개체를 사용하는지 확인해야합니다. 각 스레드에 다른 뮤텍스가 있으면 아무 일도하지 않습니다. 대신 다른 스레드가 @RichardHodges와 같이 이미 잠긴 뮤텍스를 잠그려고하면 블록됩니다. – Hayt

+0

감사합니다. – niko

답변

15

m 유형 std::mutex의 변수임을 감안할 때 다른 스레드가 동일한 m을 잠그려고 시도하고 m.unlock()을 호출 할 때까지 차단 될 것이므로 b의 증가분은 다른 스레드의 간섭으로부터 '보호'됩니다.

그리고 더 미묘한 일이 있습니다.

단일 스레드 코드에서 컴파일러는로드 및 저장 순서를 변경하려고합니다.

int a = b + 1; 
// m.lock(); 
b = a; 
// m.unlock(); 
do_something_with(a); 

심지어 :

do_something_with(++b); 

그러나 std::mutex::lock(), unlock()이 칩셋에보다 효율적으로 밝혀졌다 경우 잠금없이, 컴파일러는 효과적으로 코드를 다시 쓸 수없는 것 std::thread(), std::async(), std::future::get() 등은 펜스이다. 컴파일러는 코드를 작성할 때 지정한 위치의 펜스의 다른 쪽에서 연산이 끝나는 방식으로로드 및 저장 (읽기 및 쓰기)을 다시 정렬하지 않을 수 있음을 알고 있습니다.

1: 
2: m.lock(); <--- This is a fence 
3: b += 1; <--- So this load/store operation may not move above line 2 
4: m.unlock(); <-- Nor may it be moved below this line 

이 경우 아니었다면 무슨 일이 일어날 지 상상해

(순서가 변경된 코드) 당신이 그것을 끝까지 경우

thread1: int a = b + 1; 
    <--- Here another thread precedes us and executes the same block of code 
    thread2: int a = b + 1; 
    thread2: m.lock(); 
    thread2: b = a; 
    thread2: m.unlock(); 
thread1: m.lock(); 
thread1: b = a; 
thread1: m.unlock(); 
thread1:do_something_with(a); 
thread2:do_something_with(a); 

, 당신은 볼이 지금 잘못이있다 b를 그 이유는 컴파일러가 코드를 더 빨리 만들려고했기 때문입니다.

... 그리고 이것은 컴파일러의 최적화에 불과합니다. std::mutex 등은 또한 메모리 캐시가로드 및 저장을 더 최적의 방식으로 재정렬하는 것을 방지합니다. 이는 단일 스레드 환경에서는 좋지만 멀티 코어 (즉, 모든 최신 PC 또는 전화) 시스템에서는 재앙입니다.

스레드 B가 동일한 데이터를 읽기 전에 스레드 A의 캐시를 플러시해야하고 캐시 된 메모리 액세스에 비해 캐시를 메모리로 플러시하는 속도가 너무 느리기 때문에이 안전성에 대한 비용이 있습니다. 그러나 c'est la vie. 동시 실행을 안전하게하는 유일한 방법입니다.

가능한 경우 SMP 시스템에서 각 스레드가 작동하는 데 필요한 자체 데이터 복사본을 갖고 있기를 바랍니다. 우리는 자물쇠에서 보낸 시간뿐만 아니라 울타리를 가로 지르는 횟수를 최소화하고자합니다.

나는 std::memory_order 수식어에 대해 이야기 할 수는 있지만 전문가는 종종 어둡고 위험한 구멍이며, 초보자는 올바르게 이해할 수있는 희망이 없습니다.

+4

마지막 문장은 과장된 표현입니다. :) – Mysticial

1

리소스 사용량 측면에서 컴파일러는 mutex.lock()에 대해 덜주의 할 수 없습니다. 적절한 잠금/잠금 해제 내에서 자원 액세스가 일어나는지 확인하는 것은 프로그래머의 의무입니다.

그러나 컴파일러는 뮤텍스를 걱정하지 않는 방식으로 뮤텍스를 신경 써야하지만 그렇지 않은 특정 구조를 최적화하지는 못합니다.하지만 지금 당장이 구성에 관심이 없습니다.

6

"뮤텍스"는 "상호 배제"의 약자입니다. 뮤텍스 사용에 대한 적절한 규율은 스레드간에 공유되는 변수를 수정하는 코드를 입력하기 전에 코드를 잠그고 해당 코드 섹션이 잠금 해제 될 때 잠금을 해제하는 것입니다. 한 스레드가 뮤텍스를 잠그면 다른 스레드가 뮤텍스를 소유 한 스레드가 스레드를 잠금 해제 할 때까지 해당 스레드를 잠 그게됩니다. 즉, 한 번에 하나의 스레드 만 공유 변수를 수정할 수있는 코드 내부에 있으며 경쟁 조건이 제거됩니다.

뮤텍스의 나머지 부분은 컴파일러의 마법에 의존합니다. 컴파일러가 보호 된 코드 내부에서 외부로 데이터로드 및 저장을 이동하지 못하게합니다. 반대의 경우도 마찬가지입니다. 코드는 보호받습니다.

3

뮤텍스는 semaphore의 특정 구현입니다. 특히 바이너리 세마포어입니다.

(분명 컴퓨터 과학 컨텍스트) 세마포어가되는 정수 변수, 하드웨어 또는 소프트웨어 (시스템 동작)프리미티브로서 구현 될 수있다 원자 (인터럽트 될 수 없음). 스레드가 중단 될 수 있기 때문에이 뮤텍스가되어 테스트 한 후 그 접근 방식이 작동하지 않을 수 있습니다, 순수한 높은 수준의 언어로

int mutex = 1; // The mutex is free when created (free=1, occupied=0). 

// in a concurrency block 
{ 
    :try-again 

    // test if mutex is 1 (is free). 
    if (mutex > 0) { 

    // mutex WAS free so now I set as occupied (set 0) 
    --mutex; 
    // Now I've 'acquired' the mutex and since the mutex is 0 or 1 
    // only me can be here. 

    // Do something in mutual exclusion 
    // Done. 
    // Unlock the mutex 
    ++mutex; 
    // Now the mutex is free so other threads can acquire it. 

    } else { 
    // Mutex is 0 so I tried but it's already occupied. 
    // Block the thread and try again. 
    goto try-again; 
    } 

} 

명백한 :

(의사 높이 레벨 코드) 같은 을 상상해 무료로 사용할 수 있습니다.

이러한 이유로 세마포어와 뮤텍스는 "한 클럭"(원자 적으로)의 "테스트 및 설정"작업을 구현하는 기본 명령어를 사용하여 구현됩니다.

예는 test-and-set 프리미티브입니다.

B에서의 할당 :

int a; 
m.lock(); 
b += 1; 
a = b; 
m.unlock(); 
do_something_with(a); 

이 여기에 무슨에 '명백한'일이 있습니다 :

이 순서를 상상 :

+0

뮤텍스와 바이너리 세마포어를 상호 교환 할 수 있도록 처리하여 불타 버렸기 때문에 차이가 있습니다. 많은 스레드 라이브러리 (예 : pthread)에서 뮤텍스는 잠긴 스레드에 의해서만 잠금 해제 될 수 있지만 세마포는 모든 스레드에 의해 "잠금 해제"될 수 있습니다. –

3

그런 영리함은 전혀 없으며 올바르게 작동하게하는 것은 프로그래머의 책임입니다.

뮤텍스는 벽이없는 집에 잠글 수있는 문과 같습니다.

잠글 때 다른 사람이 집 에 들어가는 것을 막을 수 있습니다. 문이 잠길 때입니다.
모든 사람이 독점적으로 문을 통해 집에 들어가는 데 동의하고 문이 잠겨있을 때 내부 사람이 문을 열어 종료 할 때까지 기다리는 것이 유용합니다.
아무것도해서는 안되는 계약을 제외하고는 존재하지 않는 벽을 통해 나쁜 사람이 집에 들어가는 것을 금지하지 않습니다.

+1

사실은 컴파일러의 최적화 프로그램과 코드 생성기가 내포 된 메모리 펜스를 인식하고 존중한다는 사실에 있습니다. –

+0

벽이 없으면 집이별로 없다는 사실에 신경 쓰지 마십시오. – immibis

관련 문제