2012-05-09 23 views
3

考虑以下串行功能。当我并行化我的代码时,每个线程都会在并行区域内调用此函数(未显示)。我试图使这个线程安全高效(快速)。C++ OpenMP关键:“单向”锁定?

float get_stored_value__or__calculate_if_does_not_yet_exist(int A) 
{  
    static std::map<int, float> my_map; 

    std::map::iterator it_find = my_map.find(A); //many threads do this often. 

    bool found_A = it_find != my_map.end(); 

    if (found_A) 
    { 
     return it_find->second; 
    } 
    else 
    { 
     float result_for_A = calculate_value(A); //should only be done once, really. 
     my_map[A] = result_for_A; 
     return result_for_A; 
    }  
} 

几乎每一次调用这个函数时,该线程将成功地“发现”了自己的“A”存储的值(不管它是什么)。每隔一段时间,当调用“新A”时,都必须计算和存储一个值。

那么我应该把#pragma omp critical

虽然简单,它是非常低效把周围所有的这一个#pragma omp critical,因为每个线程将不断这样做的,它常常是只读的情况。

有没有办法实现“单向”critical或“单向”lock例程?也就是说,当写入my_mapelse声明时,涉及迭代器的上述操作只应该“锁定”。但是多线程应该能够同时执行.find调用。

我希望我有道理。 谢谢。

回答

1

虽然@ ChrisA的回答可能会解决您的问题,但我会在这里留下我的答案,以防将来的任何搜索者发现它有用。

如果您愿意,可以给#pragma omp critical栏目name。然后,具有该名称的任何部分都被视为相同的关键部分。如果这是你想要做的事情,你可以很容易地导致你的方法的一小部分是关键。

#pragma omp critical map_protect 
{ 
    std::map::iterator it_find = my_map.find(A); //many threads do this often. 

    bool found_A = it_find != my_map.end(); 
} 

...

#pragma omp critical map_protect 
{ 
    float result_for_A = calculate_value(A); //should only be done once, really. 
    my_map[A] = result_for_A; 
} 

#pragma omp atomic#pragma omp flush指令也可能是有用的。

atomic会导致写入内存位置(表达式前面的表达式中的左值)始终为原子。

flush确保任何预计可用于所有线程的内存实际上都写入所有线程,而不是存储在处理器高速缓存中,并且不可用于应该在的位置。

+0

但你'critical'的使用是低效我很担心 - 多线程不能simlutaneously读取地图由于'critical'。 – cmo

+0

尽管'atomic'是个好主意。那么,我只需要在写入区域周围放置一个'atomic'和'flush'? - 读取区域需要指示? – cmo

+0

@CycoMatto我知道。我以为我用'atomic'和'flush'完成了一些聪明的事情,发现了我的错误,并编辑了我的答案。它提供的信息可能对别人发现问题有用,但肯定不会提供您想要的锁定写作行为。 –

2

根据this link on Stack Overflow插入到std::map不会使迭代器失效。迭代器end()也是如此。 Here's a supporting link.

不幸的是,如果您不使用关键部分,插入可能会发生多次。此外,由于您的calculate_value例程可能在计算上很昂贵,因此您必须锁定以避免此else子句在相同的值A下运行两次,然后插入两次。

下面是一个示例函数,你可以复制这个不正确多项插入:

void testFunc(std::map<int,float> &theMap, int i) 
{ 
    std::map<int,float>::iterator ite = theMap.find(i); 

    if(ite == theMap.end()) 
    { 
     theMap[i] = 3.14 * i * i; 
    } 
} 

然后叫这样的:

std::map<int,float> myMap; 

int i; 
#pragma omp parallel for 
for(i=1;i<=100000;++i) 
{ 
    testFunc(myMap,i % 100); 
} 

if(myMap.size() != 100) 
{ 
    std::cout << "Problem!" << std::endl; 
} 

编辑:编辑在早期的版本版本来纠正错误。

+0

即使'.insert'出现“mid-find”,情况是否如此? (即当这个线程在'.find'调用中时)。 – cmo

+0

虽然插入操作可能被多次调用*,但实际写入可能会发生一次或多次,具体取决于*您使用哪种插入操作符*。使用'operator []',例如'myMap [i] = 3.14 * i * i'会导致多次写入。然而,'myMap.insert(std :: pair (i,3.14 * i * i))'实际上只写了一次。 – cmo

1

OpenMP是用于自动循环并行化的编译器“工具”,不是线程通信或同步库;所以它没有复杂的互斥体,如读/写互斥体:在写入时获取锁定,但不在读取。

这是implementation example

反正克里斯答的答案是比我好,虽然:)