簡體   English   中英

用於std :: generate_n的並行執行可變lambda生成器

[英]Parallel execution mutable lambda generator for std::generate_n

當使用在其捕獲中具有初始化器的可變lambda對std :: generate_n使用並行執行時,是否可以並行訪問初始化值線程安全?

[MCVE]

#include<vector>
#include <algorithm>
#include <execution>

int main()
{
  std::vector<int> v(1000);
  std::generate_n(std::execution::par, v.data(), v.size(), [i = 0]() mutable { return i++; });

  return 0;
}

是否可以訪問捕獲的i線程安全?

首先,讓我們看一下generate_n的簽名:

template< class ExecutionPolicy, class ForwardIt , class Size, class Generator >
ForwardIt generate_n(ExecutionPolicy&& policy, ForwardIt first, Size count, Generator g);

重要的是最后一個參數(它是你的lambda)是按值傳遞 另外你不知道它是如何在實現中內部傳遞的,所以你的lambda可能有一些副本,每個副本都有它自己的計數器。 我想這不是意圖。

在實例之間共享計數器有幾種選擇:

  1. 在lambda上使用std :: ref:

     const auto func = [i = std::atomic<int>()]() mutable -> int { return i++; }; std::vector<int> v(1000); std::generate_n(std::execution::par, v.data(), v.size(), std::ref(func)); 
  2. 在仿函數實例之間共享計數器:

     std::atomic<int> i = 0; std::vector<int> v(1000); std::generate_n(std::execution::par, v.data(), v.size(), [&i]() -> int { return i++; }); 

請注意,在這兩種情況下我都使用std :: atomic,因為您需要自己處理同步。

是否可以訪問捕獲的i線程安全?

不。客戶端代碼負責確保不會發生數據爭用。 你能做的就是這個(從cppreference復制和定制)

int i = 0;
std::mutex m;

std::generate_n(std::execution::par, v.data(), v.size(), [&]() {
    std::lock_guard<std::mutex> guard(m);    
    return i++; });

或者,如果你堅持lambda捕獲與mutable關鍵字一起:

std::generate_n(std::execution::par, v.data(), v.size(),
    [i = 0, m = std::mutex()] () mutable  {
        std::lock_guard<std::mutex> guard(m);    
        return i++; });

請注意,正如@Eric在注釋中指出的那樣,@ DmitryGordon在他的回答中指出, std::generate_n可能會復制函數對象。 這是有問題的,因為每個復制的實例都有自己的計數器i ,它獨立於其他計數器遞增。 另請注意,@ rubenvb指出std::generate_n中的函數對象的副本甚至無法編譯。 因此,第一個例子顯然是可取的,甚至可能是唯一可行的例子。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM