const는 C ++ 11에서 스레드 안전을 의미합니까?


115

C ++ 11 에서 스레드 안전const의미 한다고 들었습니다 . 사실인가요?

그 의미 하는가 const에 해당 이제 자바synchronized?

키워드가 부족 합니까?


1
C ++-faq는 일반적으로 C ++ 커뮤니티에서 관리하며 친절하게 방문하여 채팅에서 의견을 물어볼 수 있습니다.
Puppy

@DeadMG : 저는 C ++-faq과 그 에티켓을 몰랐습니다. 코멘트에서 제안되었습니다.
K-ballo 2013 년

2
const가 스레드로부터 안전하다는 것을 어디서 들었습니까?
Mark B

2
@Mark B : Herb SutterBjarne StroustrupStandard C ++ Foundation 에서 그렇게 말하고 있습니다. 답변 하단의 링크를 참조하세요.
K-ballo 2013 년

사람들에게 참고 여기에오고 : 진짜 문제가 있는지 아닙니다 const 수단의 스레드 안전합니다. 그것은 말도 안되는 일입니다. 그렇지 않으면 계속 진행하여 모든 스레드 안전 메서드를 const. 오히려 우리가 정말로 묻는 질문은 const IMPLIES 스레드로부터 안전하다는 것 입니다. 이것이 바로이 토론의 내용입니다.
user541686

답변:


131

C ++ 11 에서 스레드 안전const의미 한다고 들었습니다 . 사실인가요?

그것은이다 다소 사실 ...

이것은 표준 언어 가 스레드 안전성에 대해 말하는 것입니다.

[1.10 / 4] 두 표현식 평가중 하나가 메모리 위치 (1.7)를 수정하고 다른 하나가 동일한 메모리 위치에 액세스하거나 수정하면 충돌 합니다.

[1.10 / 21] 프로그램 실행에는서로 다른 스레드에 두 개의 충돌하는 동작이 포함되어있는경우 데이터 경합 이 포함됩니다.이 중 적어도 하나는 원자 적이 지 않고 다른 작업보다 먼저 발생하지 않습니다. 이러한 데이터 경쟁은 정의되지 않은 동작을 초래합니다.

데이터 경쟁 이 발생 하기에 충분한 조건입니다 .

  1. 주어진 사물에 대해 동시에 수행되는 두 개 이상의 작업이 있습니다. 과
  2. 그들 중 적어도 하나는 쓰기입니다.

표준 라이브러리는 조금 더가는, 그 기반으로 :

[17.6.5.9/1] 이 섹션은 데이터 경쟁 (1.10)을 방지하기 위해 구현이 충족 해야하는 요구 사항을 지정합니다. 모든 표준 라이브러리 기능은 달리 명시되지 않는 한 각 요구 사항을 충족해야합니다. 구현은 아래에 지정된 것 이외의 경우에 데이터 경합을 방지 할 수 있습니다.

[17.6.5.9/3] C ++ 표준 라이브러리 함수를 직접 또는 간접적으로 객체 객체가 함수의 비를 통해 직접 또는 간접적으로 액세스하지 않는 한 현재 스레드가 아닌 다른 스레드에서 액세스 (1.10)를 수정하지 않는다 CONST를 포함, 인수를this.

간단히 말해서 const객체에 대한 작업 이 스레드로부터 안전 할 것으로 기대한다고 말합니다 . 즉, 표준 라이브러리const자신의 유형의 객체에 대한 작업을 수행하는 한 데이터 경쟁을 도입하지 않습니다.

  1. 완전히 읽기로 구성됩니다. 즉, 쓰기가 없습니다. 또는
  2. 내부적으로 쓰기를 동기화합니다.

이러한 기대가 유형 중 하나에 적용되지 않는 경우 표준 라이브러리의 구성 요소와 함께 직접 또는 간접적으로 사용 하면 데이터 경쟁 이 발생할 수 있습니다 . 결론적으로 표준 라이브러리 관점 에서 스레드 안전const의미 합니다 . 이것은 단지 계약일 뿐이며 컴파일러에 의해 시행되지 않는다는 점에 유의하는 것이 중요합니다. 이를 깨 뜨리면 정의되지 않은 동작이 발생 하고 여러분은 혼자입니다. 존재 여부 는 적어도 데이터 경쟁 과 관련하여 코드 생성에 영향을 미치지 않습니다 .const

그 의미 하는가 const에 해당 이제 자바synchronized?

아니 . 전혀...

직사각형을 나타내는 지나치게 단순화 된 다음 클래스를 고려하십시오.

class rect {
    int width = 0, height = 0;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        width = new_width;
        height = new_height;
    }
    int area() const {
        return width * height;
    }
};

멤버 함수는 area 이다 스레드 안전성 ; 그것 때문이 const아니라 전적으로 읽기 작업으로 구성되기 때문입니다. 관련된 쓰기가 없으며 데이터 경합 이 발생하려면 최소한 하나의 쓰기가 필요 합니다. 즉, area원하는만큼 많은 스레드에서 호출 할 수 있으며 항상 올바른 결과를 얻을 수 있습니다.

이 것을 의미하지 않음을 참고 rect스레드 안전 . 실제로 특정에 대한 area호출과 동시에 호출 이 발생하면 이전 너비와 새 높이 (또는 왜곡 된 값)를 기반으로 결과를 계산할 수있는 방법 을 쉽게 알 수 있습니다. .set_sizerectarea

그러나 확실히하다, rect아니다 const는 심지어 않을 것으로 예상 있도록 스레드 안전 결국. 객체는 선언 된 const rect반면에, 것, 스레드 안전 에는 쓰기가 가능하지 않기 때문에 (그리고 당신이 고려하는 경우 const_cast원래 선언 뭔가 -ing const당신이 얻을 정의되지 않은-행동을 하고 그것 뿐이다).

그렇다면 그것은 무엇을 의미합니까?

논쟁을 위해 곱셈 연산이 매우 비싸고 가능하면 피하는 것이 좋다고 가정 해 봅시다. 요청 된 경우에만 영역을 계산 한 다음 나중에 다시 요청 될 경우 캐시 할 수 있습니다.

class rect {
    int width = 0, height = 0;

    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        cached_area_valid = ( width == new_width && height == new_height );
        width = new_width;
        height = new_height;
    }
    int area() const {
        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

[이 예는 너무 인공 보인다면, 당신은 정신적으로 대체 할 수 inta로 매우 큰 동적으로 할당 된 정수 본질적으로 비입니다 스레드 안전 하고있는 곱셈이 매우 비싸다.]

멤버 함수는 area 더 이상하지 스레드 안전 지금 쓰기를하고있다 내부적으로 동기화되지 않습니다. 이게 문제가 되나요? 에 대한 호출 은 다른 객체 area복사 생성자 의 일부로 발생할 수 있습니다. 이러한 생성자표준 컨테이너의 일부 작업에 의해 호출되었을 수 있으며 ,이 시점에서 표준 라이브러리 는이 작업이 데이터 경합 과 관련하여 읽기 로 동작 할 것으로 예상합니다. . 그러나 우리는 쓰기를하고 있습니다!

즉시 우리가를 넣어 rectA의 표준 컨테이너 --directly 또는 indirectly-- 우리는 입력하는 계약표준 라이브러리를 . const해당 계약을 준수하면서 함수 에서 쓰기를 계속하려면 해당 쓰기를 내부적으로 동기화해야합니다.

class rect {
    int width = 0, height = 0;

    mutable std::mutex cache_mutex;
    mutable int cached_area = 0;
    mutable bool cached_area_valid = true;

public:
    /*...*/
    void set_size( int new_width, int new_height ) {
        if( new_width != width || new_height != height )
        {
            std::lock_guard< std::mutex > guard( cache_mutex );
        
            cached_area_valid = false;
        }
        width = new_width;
        height = new_height;
    }
    int area() const {
        std::lock_guard< std::mutex > guard( cache_mutex );
        
        if( !cached_area_valid ) {
            cached_area = width;
            cached_area *= height;
            cached_area_valid = true;
        }
        return cached_area;
    }
};

우리가 만든 것을 주 area기능 스레드 안전을 하지만,이 rect아직되지 스레드 안전 . 를 호출하면 area호출하는 것과 같은 시간에 일어나고 set_size여전히에 할당하기 때문에, 잘못된 값을 계산 끝낼 수 widthheight뮤텍스에 의해 보호되지 않습니다.

정말로 thread-safe를 원한다면 rect동기화 프리미티브를 사용하여 non-thread-safe 를 보호합니다 rect.

키워드가 부족 합니까?

네, 그렇습니다. 그들은 첫날부터 키워드 가 부족했습니다 .


출처 : 당신이 모르는 constmutable - 허브 셔터를


6
@Ben Voigt :에 대한 C ++ 11 사양 std::string이 이미 COW를 금지하는 방식으로 표현되어 있다는 것이 제 이해입니다 . 세부 사항은 기억 나지 않지만 ...
K-ballo 2013-01-02

3
@BenVoigt : 아니요. 단지 그러한 일이 동기화되지 않도록 방지 할뿐입니다. 즉, 스레드로부터 안전하지 않습니다. C ++ 11은 이미 명시 적으로 COW를 금지합니다.이 특정 구절은 그와 관련이 없으며 COW를 금지하지 않습니다.
Puppy

2
논리적 인 차이가있는 것 같습니다. [17.6.5.9/3]은 "직간접 적으로 수정해서는 안된다"는 말로 "너무 많이"금지합니다. 원자 적 쓰기가 "수정"이 아닌 것으로 정의 되지 않는 한 , "직간접 적으로 데이터 경쟁을 도입해서는 안된다"라고 말해야 합니다. 그러나 나는 이것을 어디에서도 찾을 수 없습니다.
Andy Prowl 2013 년

1
나는 아마 여기에서 나의 요점을 조금 더 명확하게했을 것이다 : isocpp.org/blog/2012/12/… 어쨌든 도와 주셔서 감사합니다.
Andy Prowl 2013 년

1
때때로 나는 이러한 표준 단락을 작성하는 데 실제로 책임이있는 사람 (또는 직접 관련된 사람)이 누구인지 궁금합니다.
pepper_chico 2013 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.