2016-12-26 76 views
30

在傑克遜維爾會議上提案P0024r2有效採用Parallelism TS的規範被接受爲C++17 (draft)。該提議增加了許多采用執行策略參數的算法的重載,以指示應考慮哪種並行性。有在<execution>(20.19.2 [執行])已定義三個執行策略:使用STL並行算法的用戶有什麼限制?

  • std::execution::sequenced_policyconstexpr對象(20.19.4 [execpol.seq])std::execution::seq(20.19.7 [parallel.execpol.objects ])來指示順序執行,類似於調用沒有執行策略的算法。
  • std::execution::parallel_policy(20.19.5 [execpol.par])與constexpr對象std::execution::par(20.19.7 [parallel.execpol.objects])指示執行可能使用多線程的算法。
  • std::execution::parallel_unsequenced_policy(20.19.6 [execpol.vec])與constexpr對象std::execution::par_unseq(20.19.7 [parallel.execpol.objects])指示執行可能使用向量執行和/或多個線程的算法。

STL算法通常將用戶定義的對象(迭代器,函數對象)作爲參數。 對於使用標準執行策略的並行算法可以使用它們的用戶定義對象有什麼限制?

例如,當使用類似於以下示例中的算法時,對FwdItPredicate有什麼影響?

template <typename FwdIt, typename Predicate> 
FwdIt call_remove_if(FwdIt begin, FwdIt end, Predicate predicate) { 
    return std::remove_if(std::execution::par, begin, end, predicate); 
} 
+0

閱讀http://en.cppreference.com/w/cpp/algorithm/execution_policy_tag_t我明白這是用戶責任......無論如何......所以我也在等待回覆 – PiotrNycz

回答

17

簡短的回答是,元件訪問功能(基本上由各種參數的算法所要求的操作;參見以下詳細說明)使用的執行策略std::execution::parallel算法中使用不準導致數據比賽或死鎖。與使用執行策略std::execution::parallel_unsequenced_policy的算法一起使用的元素訪問函數另外不能使用任何阻塞同步。

詳情

該描述基於選票文件N4604。我還沒有核實一些條款是否根據國家機構意見進行了修改(粗略檢查似乎意味着迄今尚未進行修改)。

第25.2節[algorithms.parallel]指定了並行算法的語義。有不適用的算法多約束不採取執行策略,在多個部分細分:

  1. 在25.2.2 [algorithms.parallel.user]限制什麼樣的斷言功能,可以做他們的論點:

    函數對象傳遞到並行算法作爲Predicate類型,BinaryPredicateCompare的對象,並經由它們的參數BinaryOperation不得直接或間接地修改對象。

    子句的寫入它的方式看來,對象本身可以,只要其他約束(見下文)被服從修改。請注意,此約束獨立於執行策略,因此即使在使用std::execution::sequenced_policy時也適用。完整的答案比這更復雜,看起來規範目前無意中受到了限制(請參見下面的最後一段)。

  2. 在25.2.3 [algorithms.parallel.exec]添加上元件接入功能所特有的不同的執行策略約束(見下文):

    • 當使用std::execution::sequenced_policy元件訪問功能都是從同一個線程調用的,即執行不以任何形式交錯。
    • 使用std::execution::parallel_policy時,不同的線程可能會從不同線程同時調用元素訪問函數。從不同線程調用元素訪問函數不允許導致數據競爭或導致死鎖。然而,來自同一線程的元素訪問的調用是[不確定的]序列,即不存在來自相同線程的元素訪問功能的交錯調用。例如,如果與std::execution::par一起使用Predicate來計算它被調用的頻率,則相應的計數將需要適當地同步。
    • 使用std::execution::parallel_unsequenced_policy時,元素訪問函數的調用可以在不同線程之間以及在一個執行線程中交錯。也就是說,使用阻塞同步原語(如std::mutex)可能會導致死鎖,因爲同一線程可能會嘗試多次同步(並嘗試多次鎖定相同的互斥鎖)。當使用標準庫函數元素訪問功能,在標準的約束是(25.2.3 [algorithms.parallel.exec]段落4):

      一個標準庫函數是矢量不安全如果指定則同步與另一個函數調用,或另一個函數調用指定與它同步,如果它不是內存分配或釋放函數。矢量化 - 不安全的標準庫函數可能不會被用戶代碼調用execution::parallel_unsequenced_policy算法調用。

    • 當使用實現定義的執行策略時會發生什麼,不出所料,實現定義。

  3. 在25.2.4 [algorithm.parallel.exception]使用從元件訪問功能拋出的異常的被排序的限制:當一個元件訪問函數將拋出異常,std::terminate()被調用。也就是說,拋出異常是合法的,但結果不太可取。請注意,即使使用std::execution::sequenced_policy,也會調用std::terminate()

元訪問功能

以上使用的約束的術語元件接入功能。該術語在25.2.1 [algorithm.parallel.defns]第2段中定義。有四組列爲元素訪問函數功能:

  • ,該算法進行實例化的迭代器的類別中的所有操作。
  • 對其規範所要求的那些序列元素的操作。
  • 用戶提供的函數對象,在算法執行期間應用,如果規範要求的話。
  • 對規範要求的那些函數對象的操作。

實質上,元素訪問功能是所有這些標準明確地指的算法,或與這些算法中使用的概念的規範的操作。未被提及並且例如被檢測爲存在的功能(例如,使用SFINAE)沒有受到約束,並且實際上不能從並行算法中調用對其使用施加同步約束。

的問題

它稍微關於存在似乎不能保證[突變]元素訪問功能被應用到對象是不同的線程之間的不同。特別是,我看不到任何保證應用於迭代器對象的迭代器操作無法應用於來自兩個不同線程的同一個迭代器對象!其含義是,例如,迭代器對象上的operator++()需要以某種方式同步其狀態。我看不到如何在operator==()這些對象在不同的​​線程中被修改時做些有用的事情。看起來無意中對同一對象的操作需要同步,因爲將元素訪問函數同時應用於[mutating]元素訪問函數對於對象沒有任何意義。但是,我看不到任何文字說明使用了不同的對象(我想我需要爲此提出一個缺陷)。