2012-12-19 2 views
16

을 쓰는 데 쓰레드 안전성이없는 on Stackoverflow입니다. 그러나 실제로 그 의미는 무엇입니까? 일반 배열에 쓰기 가능한 데이터를 저장해야한다는 의미입니까?std :: vector를 쓰는 것에 대한 스레드 안전성 대 일반 배열

std::vector::push_back(element)에 대한 동시 호출은 벡터의 크기 조정을 수반 할 수 있으므로 일관성없는 데이터 구조를 초래할 수 있습니다. 그러나 크기 조정이 관여하지 않습니다 이런 경우에 대해 무엇을 :

1) 배열을 사용하여 :

int data[n]; 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

2)`표준 : vector`` 사용 :

std::vector<int> data; 
data.resize(n); 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

을 첫 번째 구현이 스레드 안전성과 b) 성능면에서 두 번째 구현보다 실제로 더 낫습니까? 나는 C 스타일 배열에 익숙하지 않기 때문에 std :: vector를 선호한다.

편집 : 쓰기를 보호하기 위해 #pragma omp atomic update을 삭제했습니다.

+1

나는 대답을하기에 충분하지 않지만'std :: vector '의 다른 요소에 쓰는 것은 thread로부터 안전하다고 확신한다. – Angew

+1

이 두 조각은 모두 동일한 스레드 안전합니다. –

+0

"실제로 그게 무슨 뜻 이죠?" : 컨테이너가 동시 쓰기 ** 쓰기 **와 일치하는 경우 * 및 * 읽기 모두에 대해 독점적으로 래치되어야 함을 의미합니다. 모든 독자가 원하는 컨테이너를 두드 리도록 할 수 있지만, 쓰기 * 잠재력이 도입되는 즉시 모든 내기가 꺼 지므로 * 다른 모든 작성자가 아닌 모든 액세스를 래치 다운해야합니다. 단일 쓰기 다중 읽기 잠금은 btw를 위해 잘 작동합니다. – WhozCraig

답변

22

둘은 모두 안전합니다. 제공되는 요소가 여러 스레드에서 액세스되지 않는 한, 당신은 괜찮습니다. 병렬 루프는 각 요소에 한 번만 액세스하므로 한 스레드에서만 액세스 할 수 있습니다.

컨테이너의 멤버 함수가 스레드로부터 안전하지 않은 공간이 표준에 있습니다. 이 경우에는 vector<int>::operator[]을 사용하므로 해당 멤버에 대한 스레드 안전성을 명시 적으로 보장해야합니다. const가 아닌 벡터에서도 호출하면 벡터 자체를 수정하지 않으므로 합리적인 것처럼 보입니다. 그래서이 경우에 문제가 있다는 것을 의심 스럽지만, 저는 보증을 찾지 않았습니다. [편집 : rici가 그것을 발견했습니다]. 잠재적으로 안전하지 않더라도 루프 전에 int *dataptr = &data.front()을 수행 한 다음 data 대신 dataptr의 인덱스를 만들 수 있습니다. 그것은 여러 요소가 하나의 객체 내부에 공존하는 특수 사건 년 이후로

옆으로,이 코드는 vector<bool>에 대한 안전 보장 하지입니다. bool의 배열은 다른 요소가 다른 "메모리 위치"(C++ 11에서는 1.7)이기 때문에 안전합니다.

+0

openmp가 제대로 작동하는지 확인하기 위해 작은 테스트 함수를 작성했습니다. '벡터 '을 사용하고, 사용 된 스레드의 수에 크기를 변경하고 (false로 초기화) 모든 값을 동시에 true로 설정합니다. 모든 값이 참이면, 함수는 모든 스레드가 올바르게 산란했음을 표시하기 위해 true를 리턴합니다. '벡터 '이 (가) 다시 쳤습니다. 다행히도, 나는 실제로 표준의 관련 부분을 최근에 읽었으므로 알아낼 수 있었지만, 누군가이 문제를 발견한다면, 그들은 벡터 '에 동시에 쓰는 것을 피할 필요가있다. – Justin

+0

이렇게되면, 각각의 개별 스레드에서'std :: vector >'밑의 개별 벡터로 push_back을 푸는 것이 안전해야합니까? –

1

중요한 점은 벡터에 액세스하는 스레드가 여러 개있는 경우 C++을 사용하여 여러 동시 쓰기로 데이터 구조가 손상되지 않도록 할 수 있다는 것입니다. 따라서 어떤 종류의 경비원을 사용해야합니다. 반면에, 프로그램이 다중 쓰레드를 사용하지 않는다면 예제처럼 보이지 않을 것입니다.

+5

그의 예제에서는 OpenMP – nogard

+2

Upvoted 0을 통해 여러 스레드를 사용하지 않습니다. 이것은 아마도 부정확하게 downvoted되었지만 삭제 된 편집 전에 게시되었습니다 : #pragma omp atomic update, 동시 쓰기를 방지하고이 대답을 컨텍스트에서 올바르게 만듭니다. 원래 게시물 –

1

이 경우 필요한 수의 값으로 벡터를 구성해야합니까? 모두 잘 작동합니다.

std::vector<int> data(n, 0); 

resize()이 잘 작동합니다. 성능은 동일합니다. 다중 스레드 액세스가 벡터를 손상시키지 않는 이유는 데이터가 그 위치에 있고 거기에서 이동하지 않기 때문입니다. OMP 스레드는 한 번에 같은 요소에 액세스하지 않습니다.

17

데이터 경주에 대한 규칙을 지정하는 C++ 11의 경우 컨테이너의 스레드 안전성이 설명됩니다. 표준의 관련 섹션은 § 23.2.2, 2 단락 :

에도 불구하고 (17.6.5.9)에서는 벡터 <bool>을 제외한 동일한 시퀀스의 다른 요소에 포함 된 객체의 내용이 동시에 수정되는 경우 데이터 레이스가 발생하지 않도록 구현해야합니다.

[주 : 크기가 1보다 큰 벡터 <xx의 경우 x [1] = 5 및 * x.begin() = 10은 데이터 경쟁없이 동시에 실행할 수 있지만 x [0] = 5 및 * x.begin() = 10이 동시에 실행되면 데이터 경합이 발생할 수 있습니다. 일반적인 규칙의 예외로 벡터 <bool> y의 경우 y [0] = true는 y [1] = true와 경쟁 할 수 있습니다. -end 노트]

언급 된 § 17.6.5.9 본질적으로 특별히 허용하지 않는 한 표준 라이브러리 인터페이스에 의해 임의의 동시 변경을 금지하고, 내가 인용 부분 허용 정확히 무엇인지를 알려줍니다 (그리고 그 사용량 포함). (17.6.5.9)를 데이터 레이스를 방지 할 목적으로, 구현의

:

질문 스티브 Jessop, § 23.2.2의 제 1 승온 때문에 명시 시퀀스 컨테이너 []의 동시 사용을 허용 는 다음과 같은 함수를 const로 간주해야한다 : 연관, 정렬되지 않은 연관 컨테이너, 연산자 []를 제외하고, begin, end, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at 및를 고려해야한다.