병렬 컨테이너 및 개체

PPL(병렬 패턴 라이브러리)에는 해당 요소에 대한 스레드로부터 안전한 액세스를 제공하는 여러 컨테이너 및 개체가 포함되어 있습니다.

동시 컨테이너가장 중요한 작업에 대한 동시성 안전 액세스를 제공합니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다. 이러한 컨테이너의 기능은 C++ 표준 라이브러리에서 제공하는 기능과 유사합니다. 예를 들어 동시성::concurrent_vector 클래스는 std::vector 클래스와 유사합니다. 단, concurrent_vector 클래스를 사용하면 요소를 병렬로 추가할 수 있습니다. 동일한 컨테이너에 대한 읽기 및 쓰기 액세스가 모두 필요한 병렬 코드가 있는 경우 동시 컨테이너를 사용합니다.

동시 개체는 구성 요소 간에 동시에 공유됩니다. 동시 개체의 상태를 병렬로 계산하는 프로세스는 동일한 상태를 직렬로 계산하는 다른 프로세스와 동일한 결과를 생성합니다. 동시성::결합 가능한 클래스는 동시 개체 형식의 한 예입니다. 이 combinable 클래스를 사용하면 계산을 병렬로 수행한 다음, 해당 계산을 최종 결과로 결합할 수 있습니다. 동기화 메커니즘(예: 뮤텍스)을 사용하여 공유 변수 또는 리소스에 대한 액세스를 동기화하는 경우 동시 개체를 사용합니다.

섹션

이 항목에서는 다음과 같은 병렬 컨테이너 및 개체에 대해 자세히 설명합니다.

동시 컨테이너:

동시 개체:

concurrent_vector 클래스

동시성::concurrent_vector 클래스는 std::vector 클래스와 마찬가지로 해당 요소에 임의로 액세스할 수 있는 시퀀스 컨테이너 클래스입니다. 이 concurrent_vector 클래스는 동시성 안전 추가 및 요소 액세스 작업을 사용하도록 설정합니다. 추가 작업은 기존 포인터 또는 반복기를 무효화하지 않습니다. 반복기 액세스 및 순회 작업도 동시성 안전성입니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다.

concurrent_vector 벡터와 벡터 간의 차이점

클래스는 concurrent_vector 클래스와 매우 유사합니다 vector . 개체에 대한 concurrent_vector 추가, 요소 액세스 및 반복기 액세스 작업의 복잡성은 개체와 동일합니다 vector . 다음 점은 차이점 concurrent_vector 을 보여 줍니다.vector

  • 개체에 대한 concurrent_vector 추가, 요소 액세스, 반복기 액세스 및 반복기 순회 작업은 동시성 안전입니다.

  • 개체의 concurrent_vector 끝에만 요소를 추가할 수 있습니다. 클래스는 concurrent_vector 메서드를 insert 제공하지 않습니다.

  • 개체에 concurrent_vector 추가할 때 개체는 이동 의미 체계를 사용하지 않습니다.

  • 클래스는 concurrent_vector 또는 pop_back 메서드를 erase 제공하지 않습니다. 마찬가지로 vectorclear 메서드를 사용하여 개체에서 concurrent_vector 모든 요소를 제거합니다.

  • 클래스는 concurrent_vector 해당 요소를 메모리에 연속적으로 저장하지 않습니다. 따라서 배열을 concurrent_vector 사용할 수 있는 모든 방법으로 클래스를 사용할 수 없습니다. 예를 들어 형식concurrent_vector이라는 v 변수의 경우 식 &v[0]+2 은 정의되지 않은 동작을 생성합니다.

  • 클래스는 concurrent_vector grow_bygrow_to_at_least 메서드를 정의합니다. 이러한 메서드는 동시성 안전성을 제외하고 크기 조정 메서드와 유사합니다.

  • 개체에 concurrent_vector 추가하거나 크기를 조정할 때 개체의 요소를 재배치하지 않습니다. 이렇게 하면 기존 포인터 및 반복기가 동시 작업 중에 유효하지 기본 수 있습니다.

  • 런타임은 형식bool에 대한 특수 버전을 concurrent_vector 정의하지 않습니다.

동시성 금고 작업

개체의 크기를 추가하거나 늘리거나 개체의 concurrent_vector 요소에 concurrent_vector 액세스하는 모든 메서드는 동시성 안전입니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다. 이 규칙의 예외는 메서드입니다 resize .

다음 표에서는 동시성이 안전한 일반적인 concurrent_vector 메서드 및 연산자를 보여 줍니다.

런타임에서 C++ 표준 라이브러리와의 호환성을 위해 제공하는 작업(예 reserve: 동시성)은 안전하지 않습니다. 다음 표에서는 동시성이 안전하지 않은 일반적인 메서드 및 연산자를 보여 줍니다.

기존 요소의 값을 수정하는 작업은 동시성이 안전하지 않습니다. reader_writer_lock 개체와 같은 동기화 개체를 사용하여 동시 읽기 및 쓰기 작업을 동일한 데이터 요소에 동기화합니다. 동기화 개체에 대한 자세한 내용은 동기화 데이터 구조를 참조 하세요.

사용하기 concurrent_vector위해 사용하는 vector 기존 코드를 변환하는 경우 동시 작업으로 인해 애플리케이션의 동작이 변경될 수 있습니다. 예를 들어 개체에 대해 두 작업을 concurrent_vector 동시에 수행하는 다음 프로그램을 고려해 보세요. 첫 번째 작업은 개체에 추가 요소를 concurrent_vector 추가합니다. 두 번째 작업은 동일한 개체에 있는 모든 요소의 합계를 계산합니다.

// parallel-vector-sum.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_vector.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   // Create a concurrent_vector object that contains a few
   // initial elements.
   concurrent_vector<int> v;
   v.push_back(2);
   v.push_back(3);
   v.push_back(4);
   
   // Perform two tasks in parallel.
   // The first task appends additional elements to the concurrent_vector object.
   // The second task computes the sum of all elements in the same object.

   parallel_invoke(
      [&v] { 
         for(int i = 0; i < 10000; ++i)
         {
            v.push_back(i);
         }
      },
      [&v] {
         combinable<int> sums;
         for(auto i = begin(v); i != end(v); ++i) 
         {
            sums.local() += *i;
         }     
         wcout << L"sum = " << sums.combine(plus<int>()) << endl;
      }
   );
}

end 메서드는 동시성이 안전하지만 push_back 메서드를 동시에 호출하면 반환 end 된 값이 변경됩니다. 반복기가 트래버스하는 요소의 수는 확정되지 않습니다. 따라서 이 프로그램은 실행할 때마다 다른 결과를 생성할 수 있습니다. 요소 형식이 사소하지 않은 경우 경합 조건이 호출 간에 push_backend 존재할 수 있습니다. 메서드는 end 할당되었지만 완전히 초기화되지 않은 요소를 반환할 수 있습니다.

예외 금고ty

증가 또는 할당 작업에서 예외를 throw하면 개체의 concurrent_vector 상태가 유효하지 않습니다. 달리 명시되지 않는 한 concurrent_vector 잘못된 상태에 있는 개체의 동작은 정의되지 않습니다. 그러나 소멸자가 개체가 잘못된 상태에 있더라도 개체가 할당하는 메모리를 항상 해제합니다.

벡터 요소 T의 데이터 형식은 다음 요구 사항을 충족해야 합니다. 그렇지 않으면 클래스의 동작이 concurrent_vector 정의되지 않습니다.

  • 소멸자는 throw하지 않아야 합니다.

  • 기본 또는 복사 생성자가 throw되는 경우 소멸자는 키워드(keyword) 사용하여 virtual 선언하지 않아야 하며 초기화되지 않은 메모리에서 올바르게 작동해야 합니다.

[맨 위로 이동]

concurrent_queue 클래스

동시성::concurrent_queue 클래스는 std::queue 클래스와 마찬가지로 앞뒤 요소에 액세스할 수 있습니다. 이 concurrent_queue 클래스를 사용하면 동시성 안전 큐에 넣기 및 큐에서 제거 작업을 수행할 수 있습니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다. 이 클래스는 concurrent_queue 동시성 안전이 아닌 반복기 지원도 제공합니다.

concurrent_queue 큐의 차이점

클래스는 concurrent_queue 클래스와 매우 유사합니다 queue . 다음 점은 차이점 concurrent_queue 을 보여 줍니다.queue

  • 개체에 대한 concurrent_queue 큐에 넣기 및 큐에서 제거 작업은 동시성 안전입니다.

  • 클래스는 concurrent_queue 동시성로부터 안전하지 않은 반복기 지원을 제공합니다.

  • 클래스는 concurrent_queue 또는 pop 메서드를 front 제공하지 않습니다. 클래스는 concurrent_queue try_pop 메서드를 정의하여 이러한 메서드를 대체합니다.

  • 클래스는 concurrent_queue 메서드를 back 제공하지 않습니다. 따라서 큐의 끝을 참조할 수 없습니다.

  • 클래스는 concurrent_queue 메서드 대신 unsafe_size 메서드를 size 제공합니다. 메서드 unsafe_size 는 동시성 안전 하지 않습니다.

동시성 금고 작업

개체에 큐에 추가하거나 개체에서 concurrent_queue 큐에서 제거하는 모든 메서드는 동시성으로부터 안전합니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다.

다음 표에서는 동시성이 안전한 일반적인 concurrent_queue 메서드 및 연산자를 보여 줍니다.

empty 메서드는 동시성 안전이지만 동시 작업으로 인해 메서드가 반환되기 전에 empty 큐가 증가하거나 축소될 수 있습니다.

다음 표에서는 동시성이 안전하지 않은 일반적인 메서드 및 연산자를 보여 줍니다.

반복기 지원

concurrent_queue 동시성이 안전하지 않은 반복기를 제공합니다. 디버깅에만 이러한 반복기를 사용하는 것이 좋습니다.

concurrent_queue 반복기는 정방향으로만 요소를 트래버스합니다. 다음 표에서는 각 반복기가 지원하는 연산자를 보여 줍니다.

연산자 설명
operator++ 큐의 다음 항목으로 이동합니다. 이 연산자는 증분 전 및 증분 후 의미 체계를 모두 제공하기 위해 오버로드됩니다.
operator* 현재 항목에 대한 참조를 검색합니다.
operator-> 현재 항목에 대한 포인터를 검색합니다.

[맨 위로 이동]

concurrent_unordered_map 클래스

동시성::concurrent_unordered_map 클래스는 std::unordered_map 클래스와 마찬가지로 std::p air<const Key, Ty> 형식 요소의 다양한 길이 시퀀스를 제어하는 결합 컨테이너 클래스입니다. 순서가 지정되지 않은 맵을 키와 값 쌍을 키별로 추가하거나 조회할 수 있는 사전으로 간주합니다. 이 클래스는 공유 컨테이너에 동시에 액세스하거나, 컨테이너에 삽입하거나, 업데이트해야 하는 여러 스레드 또는 태스크가 있는 경우에 유용합니다.

다음 예제에서는 사용 하 여 기본 구조를 보여 있습니다 concurrent_unordered_map. 다음은 ['a', 'i'] 범위에 문자 키를 삽입하는 예제입니다. 작업 순서가 결정되지 않으므로 각 키에 대한 최종 값도 결정되지 않습니다. 그러나 병렬로 삽입을 수행하는 것이 안전합니다.

// unordered-map-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_map<char, int> map; 

    parallel_for(0, 1000, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 751] [i, 755] [a, 756] [c, 758] [g, 753] [f, 752] [b, 757] [d, 750] [h, 754]
*/

맵을 수행하고 병렬로 작업을 줄이는 데 사용하는 concurrent_unordered_map 예제는 방법: 병렬로 맵 및 축소 작업 수행을 참조하세요.

concurrent_unordered_map unordered_map 간의 차이점

클래스는 concurrent_unordered_map 클래스와 매우 유사합니다 unordered_map . 다음 점은 차이점 concurrent_unordered_map 을 보여 줍니다.unordered_map

  • , , 및 메서드의 erase이름은 unsafe_erase각각 , unsafe_bucket_countunsafe_bucketunsafe_bucket_size입니다.bucket_sizebucket_countbucket 명명 규칙은 unsafe_ 이러한 메서드가 동시성로부터 안전하지 않음을 나타냅니다. 동시성 안전에 대한 자세한 내용은 동시성 금고 작업을 참조하세요.

  • 삽입 작업은 기존 포인터 또는 반복기를 무효화하지 않으며 맵에 이미 있는 항목의 순서를 변경하지도 않습니다. 삽입 및 트래버스 작업은 동시에 발생할 수 있습니다.

  • concurrent_unordered_map 는 전달 반복만 지원합니다.

  • 삽입은 .에서 반환 equal_range되는 반복기를 무효화하거나 업데이트하지 않습니다. 삽입은 범위의 끝에 같지 않은 항목을 추가할 수 있습니다. 시작 반복기는 동일한 항목을 가리킵니다.

교착 상태를 방지하기 위해 메모리 할당자, 해시 함수 또는 기타 사용자 정의 코드를 호출할 때 잠금을 보유하는 메서드 concurrent_unordered_map 는 없습니다. 또한 해시 함수가 항상 동일한 값과 동일한 키를 평가하도록 해야 합니다. 최상의 해시 함수는 해시 코드 공간에 키를 균일하게 분산합니다.

동시성 금고 작업

클래스를 concurrent_unordered_map 사용하면 동시성 안전 삽입 및 요소 액세스 작업을 수행할 수 있습니다. 삽입 작업은 기존 포인터 또는 반복기를 무효화하지 않습니다. 반복기 액세스 및 순회 작업도 동시성 안전성입니다. 여기서 동시성 안전은 포인터 또는 반복기가 항상 유효함을 의미합니다. 요소 초기화 또는 특정 순회 순서를 보장하지 않습니다. 다음 표에서는 동시성이 안전한 일반적으로 사용되는 concurrent_unordered_map 메서드 및 연산자를 보여 줍니다.

count 동시에 실행되는 스레드에서 메서드를 안전하게 호출할 수 있지만 새 값이 컨테이너에 동시에 삽입되는 경우 다른 스레드가 다른 결과를 받을 수 있습니다.

다음 표에서는 동시성이 안전하지 않은 일반적으로 사용되는 메서드 및 연산자를 보여 줍니다.

이러한 메서드 외에도 시작하는 unsafe_ 모든 메서드는 동시성 안전성이 아닙니다.

[맨 위로 이동]

concurrent_unordered_multimap 클래스

동시성::concurrent_unordered_multimap 클래스는 여러 값이 동일한 키에 매핑될 수 있다는 점을 제외하고 클래스와 유사 concurrent_unordered_map 합니다. 또한 다음과 같은 방법으로 다릅니다 concurrent_unordered_map .

다음 예제에서는 사용 하 여 기본 구조를 보여 있습니다 concurrent_unordered_multimap. 다음은 ['a', 'i'] 범위에 문자 키를 삽입하는 예제입니다. concurrent_unordered_multimap 를 사용하면 키가 여러 값을 가질 수 있습니다.

// unordered-multimap-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_map.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the map in parallel.

    concurrent_unordered_multimap<char, int> map; 

    parallel_for(0, 10, [&map](int i) {
        char key = 'a' + (i%9); // Geneate a key in the range [a,i].
        int value = i;          // Set the value to i.
        map.insert(make_pair(key, value));
    });

    // Print the elements in the map.
    for_each(begin(map), end(map), [](const pair<char, int>& pr) {
        wcout << L"[" << pr.first << L", " << pr.second << L"] ";
    });
}
/* Sample output:
    [e, 4] [i, 8] [a, 9] [a, 0] [c, 2] [g, 6] [f, 5] [b, 1] [d, 3] [h, 7]
*/

[맨 위로 이동]

concurrent_unordered_set 클래스

동시성::concurrent_unordered_set 클래스는 키 및 값 쌍 대신 값을 관리한다는 점을 제외하고 클래스와 매우 유사 concurrent_unordered_map 합니다. 클래스는 concurrent_unordered_set 메서드를 at 제공하지 operator[] 않습니다.

다음 예제에서는 사용 하 여 기본 구조를 보여 있습니다 concurrent_unordered_set. 다음은 ['a', 'i'] 범위에 문자 값을 삽입하는 예제입니다. 삽입을 병렬로 수행하는 것이 안전합니다.

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_set<char> set; 

    parallel_for(0, 10000, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [i] [a] [c] [g] [f] [b] [d] [h]
*/

[맨 위로 이동]

concurrent_unordered_multiset 클래스

동시성::concurrent_unordered_multiset 클래스는 중복 값을 허용한다는 점을 제외하고 클래스와 매우 유사 concurrent_unordered_set 합니다. 또한 다음과 같은 방법으로 다릅니다 concurrent_unordered_set .

  • concurrent_unordered_multiset::insert 메서드는 .std::pair<iterator, bool>

  • 클래스는 concurrent_unordered_multiset 메서드를 at 제공하지 operator[] 않습니다.

다음 예제에서는 사용 하 여 기본 구조를 보여 있습니다 concurrent_unordered_multiset. 다음은 ['a', 'i'] 범위에 문자 값을 삽입하는 예제입니다. concurrent_unordered_multiset 를 사용하면 값이 여러 번 발생합니다.

// unordered-set-structure.cpp
// compile with: /EHsc
#include <ppl.h>
#include <concurrent_unordered_set.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain() 
{
    //
    // Insert a number of items into the set in parallel.

    concurrent_unordered_multiset<char> set; 

    parallel_for(0, 40, [&set](int i) {
        set.insert('a' + (i%9)); // Geneate a value in the range [a,i].
    });

    // Print the elements in the set.
    for_each(begin(set), end(set), [](char c) {
        wcout << L"[" << c << L"] ";
    });
}
/* Sample output:
    [e] [e] [e] [e] [i] [i] [i] [i] [a] [a] [a] [a] [a] [c] [c] [c] [c] [c] [g] [g]
    [g] [g] [f] [f] [f] [f] [b] [b] [b] [b] [b] [d] [d] [d] [d] [d] [h] [h] [h] [h]
*/

[맨 위로 이동]

combinable 클래스

동시성::결합 가능한 클래스는 세분화된 계산을 수행한 다음 해당 계산을 최종 결과로 병합할 수 있는 재사용 가능한 스레드 로컬 스토리지를 제공합니다. combinable 개체는 환산(reduction) 변수로 간주될 수 있습니다.

combinable 클래스는 여러 스레드 또는 작업 간에 공유되는 리소스가 있는 경우에 유용합니다. 이 클래스는 combinable 공유 리소스에 대한 액세스를 잠금 없는 방식으로 제공하여 공유 상태를 제거하는 데 도움이 됩니다. 따라서 이 클래스는 동기화 메커니즘(예: 뮤텍스)을 사용하여 여러 스레드의 공유 데이터에 대한 액세스를 동기화하는 대안을 제공합니다.

메서드 및 기능

다음 표에서는 클래스의 몇 가지 중요한 메서드를 보여 줍니다 combinable . 모든 클래스 메서드에 combinable 대한 자세한 내용은 결합 가능한 클래스를 참조 하세요.

메서드 설명
local 현재 스레드 컨텍스트와 연결된 지역 변수에 대한 참조를 검색합니다.
clear 개체에서 모든 스레드 지역 변수를 combinable 제거합니다.
combine

combine_each
제공된 결합 함수를 사용하여 모든 스레드-로컬 계산 집합에서 최종 값을 생성합니다.

combinable 클래스는 최종 병합된 결과에서 매개 변수가 있는 템플릿 클래스입니다. 기본 생성자를 T 호출하는 경우 템플릿 매개 변수 형식에는 기본 생성자 및 복사 생성자가 있어야 합니다. 템플릿 매개 변수 형식에 T 기본 생성자가 없는 경우 초기화 함수를 매개 변수로 사용하는 오버로드된 생성자 버전을 호출합니다.

결합 또는 combine_each 메서드를 combinable 호출한 후 개체에 추가 데이터를 저장할 수 있습니다. 메서드와 combine_each 메서드를 여러 번 호출할 combine 수도 있습니다. 개체의 로컬 값이 combinable 변경되지 combine 않으면 메서드는 combine_each 호출될 때마다 동일한 결과를 생성합니다.

예제

클래스를 사용하는 방법에 대한 예제는 combinable 다음 항목을 참조하세요.

[맨 위로 이동]

방법: 병렬 컨테이너를 사용하여 효율성 향상
병렬 컨테이너를 사용하여 데이터를 병렬로 효율적으로 저장하고 액세스하는 방법을 보여 줍니다.

방법: combinable을 사용하여 성능 개선
클래스를 combinable 사용하여 공유 상태를 제거하고 성능을 향상시키는 방법을 보여 줍니다.

방법: combinable을 사용하여 집합 결합
함수를 combine 사용하여 스레드 로컬 데이터 집합을 병합하는 방법을 보여줍니다.

PPL(병렬 패턴 라이브러리)
동시 애플리케이션 개발을 위해 확장성과 사용 편의성을 높이는 명령적 프로그래밍 모델을 제공하는 PPL에 대해 설명합니다.

참조

concurrent_vector 클래스

concurrent_queue 클래스

concurrent_unordered_map 클래스

concurrent_unordered_multimap 클래스

concurrent_unordered_set 클래스

concurrent_unordered_multiset 클래스

combinable 클래스