在多线程环境中从STL Map读取/写入

时间:2010-10-01 04:26:16

标签: c++ multithreading stl pthreads

问题:我需要编写一个函数,该函数从地图返回输入键的值。如果函数找不到map中的值,它将从数据库中获取值,写入map以供将来使用并返回相同的值。可以有多个线程调用此函数。

我正在考虑这条线:

string GetData (const int key)
{

    pthread_rwlock_rdlock(&rwlock); //read lock
    string result = "not found";
    my_map::const_iterator iter = m.find(key);
    if ( iter != m.end() )//found
    {
       result = iter->second;
    }
    else //missing
    {
        pthread_rwlock_wrlock(&rwlock); // write lock
        //fetch value from data base


        //if successful, add to the map
          m[key] = "missing data";
          result = "missing data";
     pthread_rwlock_unlock(&rwlock); // unlock write lock
    }
    pthread_rwlock_unlock(&rwlock); // unlock read lock
    return result;
}

这个功能线程安全吗?两个或多个线程是否有可能在写锁定时排队并从数据库中查询相同的密钥?如果是,我该如何避免这种情况?

3 个答案:

答案 0 :(得分:7)

此函数不是线程安全的,因为它会导致未定义的行为。当您尝试获取写锁定时,您已经拥有读锁定。来自the documentation for pthread_rwlock_wrlock:

  

如果调用线程在调用[pthread_rwlock_wrlock]时保持读写锁定(无论是读取还是写入锁定),结果都是未定义的。

此解决方案也不例外。如果在保持锁定时抛出异常,则不会释放锁定,并且您的应用程序无疑会死锁。您应该使用C ++线程库(Boost.Thread,OpenThreads,just :: thread或类似的东西),它提供面向C ++的设计,支持scoped_lock(或lock_guard)等内容。

至于使算法正确,你需要的东西是:

obtain read lock
attempt to find object
if object exists
    return object
else
    release read lock
    obtain write lock
    if object exists
        return object
    else
        insert object
        return object

[如果你使用某种lock_guard,你不必担心在你返回时释放持有的锁]

答案 1 :(得分:1)

未正确实施。由于担心死锁,您仍然无法在保持读锁定的同时执行写锁定。在我的Linux机器上的pthread_rwlock_wrlock手册页中:

  

pthread_rwlock_wrlock()函数应该对其应用写锁定        rwlock引用的读写锁。调用线程获取         如果没有其他线程(读取器或写入器)保持读写,则写入锁定         锁定rwlock。否则,线程将阻塞直到它可以获取         锁。 如果在呼叫时,调用线程可能会死锁         使它保持读写锁(无论是读还是写锁)。

此外,您应该检查调用的返回值...例如,对于同时读取器的数量存在实现定义的限制。

异常安全也存在​​常见问题......考虑范围保护或尝试/捕获阻止。

答案 2 :(得分:0)

一旦获得写锁定,您可能会修复它再次查找该值。这应该足以解决您描述的问题。类似的东西:

string GetData (const int key)
{

    pthread_rwlock_rdlock(&rwlock); //read lock
    string result = "not found";
    my_map::const_iterator iter = m.find(key);
    if ( iter != m.end() )//found
    {
        result = iter->second;
    }
    else //missing
    {
        // change from read mode to write mode
        pthread_rwlock_unlock(&rwlock); // unlock read lock
        pthread_rwlock_wrlock(&rwlock); // write lock

        // Try again
        iter = m.find(key);
        if (iter != m.end()) {
            result = iter->second;
        } else {
            //if successful, add to the map
            m[key] = "missing data";
            result = "missing data";
        }
    }
    pthread_rwlock_unlock(&rwlock); // unlock read/write lock
    return result;
}