我使用OpenMP来并行化我们的C ++库。在那里,我们有各种各样的地方,我们通过将结果存储在变量中来避免重新计算某些东西(即缓存结果以便重复使用)。但是,此类行为在类的方法中对用户隐藏。例如,在第一次使用方法时,将填充缓存。所有后续使用都只是从缓存中读取。
我现在的问题是,在多线程程序中,多个线程可以同时调用这样的方法,从而导致创建/访问缓存的竞争条件。我目前通过将缓存内容放在一个关键部分来解决这个问题,但这当然会减慢一切。
示例类可能如下
class A {
public:
A() : initialized(false)
{}
int get(int a)
{
#pragma omp critical(CACHING)
if (!initialized)
initialize_cache();
return cache[a];
}
private:
bool initialized;
void initialize_cache()
{
// do some heavy stuff
initialized=true;
}
int *cache;
};
如果关键部分在initialize_cache()函数中会更好,因为它只会在缓存尚未初始化时(即仅一次)锁定所有线程,但这似乎很危险多个线程可能正在尝试同时初始化缓存。
有任何改进建议吗?理想情况下,该解决方案将与较旧的OpenMP版本兼容(即使是Visual Studio的v2 ......)
PS:这可能以前曾被问过,但是搜索openmp和缓存会给处理器缓存带来很多东西,这不是我想知道的......答案 0 :(得分:2)
您可以将"Double-Checked-Locking(DCL) pattern"与OpenMP原子操作结合使用,需要OpenMP v3.1或更高版本(read
write
pragma omp atomic
选项。
class A {
public:
A() : initialized(false)
{}
int get(int a)
{
bool b;
#pragma omp atomic read
b = initialized;
if (!b) {
#pragma omp critical(CACHING)
// you must recheck in critical section
if (!initialized)
initialize_cache();
}
return cache[a];
}
private:
bool initialized;
void initialize_cache()
{
// do some heavy stuff
#pragma omp atomic write
initialized = true;
}
int *cache;
};
...但我建议使用以下选项之一而不是DCL模式:
pthread_once()
(POSIX线程库)std::call_once()
(C ++ 11标准库)static
变量(C ++ 11核心语言特性)答案 1 :(得分:1)
高效的单身人士是您的最佳选择。 请点击此处。efficient thread-safe singleton in C++
此外,Herb Sutter talks about that in CppCon 2014
以下是我在上面显示的视频中的完整代码段:
class Foo {
public:
static Foo* Instance();
private:
Foo() {init();}
void init() { cout << "init done." << endl;} // your init cache function.
static atomic<Foo*> pinstance;
static mutex m_;
};
atomic<Foo*> Foo::pinstance { nullptr };
std::mutex Foo::m_;
Foo* Foo::Instance() {
if(pinstance == nullptr) {
lock_guard<mutex> lock(m_);
if(pinstance == nullptr) {
pinstance = new Foo();
}
}
return pinstance;
}
在此处运行代码:http://ideone.com/olvK13