C++ tbb:并发哈希映射<;K、 V>;:英特尔线程构建块(TBB)示例代码

C++ tbb:并发哈希映射<;K、 V>;:英特尔线程构建块(TBB)示例代码,c++,multithreading,concurrency,c++17,tbb,C++,Multithreading,Concurrency,C++17,Tbb,正在查找要使用英特尔线程构建块(tbb)中的tbb::concurrent_hash_map的示例代码 我可以插入,但我似乎无法读回这些值 在示例代码方面似乎有点缺乏 更新 最好的文档是Voss用的“TBB:C++并行编程,带有线程构建块”。(这是公共领域) 忽略英特尔文档。它们本质上是函数签名的集合。是开源的,并且: 为了安装TBB,我使用了与Linux、Windows和Mac兼容的TBB。是的,vcpkg来自微软,但它是100%跨平台、开源的,并且非常流行 Linux: ./vcpkg se

正在查找要使用英特尔线程构建块(tbb)中的
tbb::concurrent_hash_map
的示例代码

我可以插入,但我似乎无法读回这些值

在示例代码方面似乎有点缺乏

更新

最好的文档是Voss用的“TBB:C++并行编程,带有线程构建块”。(这是公共领域)

忽略英特尔文档。它们本质上是函数签名的集合。

是开源的,并且:

为了安装TBB,我使用了与
Linux
Windows
Mac
兼容的TBB。是的,vcpkg来自微软,但它是100%跨平台、开源的,并且非常流行

Linux:

./vcpkg search tbb              # Find the package.
./vcpkg install tbb:x64-linux   # Install the package.
窗口:

vcpkg search tbb                # Find the package.
vcpkg install tbb:x64-windows   # Install the package.
汇编:

  • 兼容任何现代编译器,包括MSVC、GCC、LLVM、英特尔编译器(ICC)等。我在
    GCC
    中使用了
    CMake
还可以下载源代码,并将头文件和库提取到源代码树中,这同样有效

代码

其他哈希映射
  • 见:
  • 请参阅:
    std::无序映射
    。这有一个更标准的API,在许多情况下是线程安全的,请参阅:。如果可能的话,建议使用它,因为它有一个更简单的API
  • 还有来自英特尔TBB的
    并发\u无序\u映射。它本质上是一样的,一个键/值映射。但是,它更老,级别更低,更难使用。必须提供哈希器、相等运算符和分配器。任何地方都没有示例代码,即使在英特尔官方文档中也是如此。尽管我偶尔尝试了几个月,但我从未让它工作过。它可能已经过时了,因为在上述免费书中没有提到它(它只包括
    并发散列图
    )。不推荐
更新:读写器锁定 实际上有两个访问器,一个是读锁,一个是写锁:

  • const\u访问器
  • accessor
如果使用
查找
,请使用
常量访问器
,它是一个读锁。如果使用
插入
擦除
,请使用
访问器
,它是一个写锁(即,它将等待任何读取完成,并阻止进一步读取,直到完成)

这实际上相当于一个,但在一个字典键上的口述,而不是整个字典

更新 学习曲线的最后一部分:对于键写入,在访问器超出范围之前不会发生任何事情。因此,任何锁都只针对几条机器指令,可能使用CAS(比较和交换)

与数据库相比,访问器的作用域类似于事务。当访问器超出范围时,整个事务将提交到hashmap

更新 上面提到的免费书籍在
并发散列映射
一章中提供了精彩的性能技巧

结论
此哈希映射的API功能强大,但有点笨拙。但是,它在插入/更新时支持细粒度的每密钥锁。任何锁都只能在少数机器指令下使用。在任何语言中,这是很少有其他哈希映射能够提供的。为简单起见,建议从
std::无序_-map
开始;它是。如果需要极快的性能,可以选择重构,或者在顶部编写一个兼容的包装器,其中包含
[]
访问器和
插入或更新()

我的意见是:如果英特尔的官方文档不那么不透明,他们的API的采用率会更高,但我猜英特尔仍想向企业客户出售其TBB咨询服务。就文档而言,免费书籍非常完美。
vcpkg search tbb                # Find the package.
vcpkg install tbb:x64-windows   # Install the package.
#include "tbb/concurrent_hash_map.h" // For concurrent hash map.

tbb::concurrent_hash_map<int, string> dict;
typedef tbb::concurrent_hash_map<int, string>::accessor dictAccessor; // See notes on accessor below.   

print("  - Insert key, method 1:\n");   
dict.insert({1,"k1"});
print("    - 1: k1\n");

print("  - Insert key, method 2:\n");
dict.emplace(2,"k2");
print("    - 2: k2\n");

string result;

{
    print("  - Read an existing key:\n");   
    dictAccessor accessor;
    const auto isFound = dict.find(accessor, 2);
    // The accessor functions as:
    // (a) a fine-grained per-key lock (released when it goes out of scope).
    // (b) a method to read the value.
    // (c) a method to insert or update the value.
    if (isFound == true) {
        print("    - {}: {}\n", accessor->first, accessor->second);
    }
}

{
    print("  - Atomically insert or update a key:\n");  
    dictAccessor accessor;
    const auto itemIsNew = dict.insert(accessor, 4);
    // The accessor functions as:
    // (a) a fine-grained per-key lock (released when it goes out of scope).
    // (b) a method to read the value.
    // (c) a method to insert or update the value.
    if (itemIsNew == true) {
        print("    - Insert.\n");
        accessor->second = "k4";
    }
    else {
        print("    - Update.\n");
        accessor->second = accessor->second + "+update";
    }
    print("    - {}: {}\n", accessor->first, accessor->second);     
}

{
    print("  - Atomically insert or update a key:\n");          
    dictAccessor accessor;
    const auto itemIsNew = dict.insert(accessor, 4);
    // The accessor functions as:
    // (a) a fine-grained per-key lock which is released when it goes out of scope.
    // (b) a method to read the value.
    // (c) a method to insert or update the value.
    if (itemIsNew == true) {
        print("    - Insert.\n");
        accessor->second = "k4";
    }
    else {
        print("    - Update.\n");
        accessor->second = accessor->second + "+update";
    }
    print("    - {}: {}\n", accessor->first, accessor->second);     
}

{
    print("  - Read the final state of the key:\n");            
    dictAccessor accessor;
    const auto isFound = dict.find(accessor, 4);
    print("    - {}: {}\n", accessor->first, accessor->second);
}
- Insert key, method 1:
  - 1: k1
- Insert key, method 2:
  - 2: k2
- Read an existing key:
  - 2: k2
- Atomically insert or update a key:
  - Insert.
  - 4: k4
- Atomically insert or update a key:
  - Update.
  - 4: k4+update
- Read the final state of the key:
  - 4: k4+update