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

发布于 2024-09-25 12:03:50 字数 822 浏览 0 评论 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
    {
        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;
}

这个函数线程安全吗?两个或多个线程是否可以在写锁上排队并从数据库中查询相同的键?如果是,我该如何避免这种情况?

Problem: I need to write a function which returns a value for a input key from a map. If function can't found the value in map it will fetch the value from database, write into map for future use and return the same. There can be multiple threads calling this function.

I am thinking on this line:

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;
}

Is this function thread safe? Isn't it possible for two or more threads to queue up on write lock and query the same key from database? If yes, how can I avoid that scenario?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

椒妓 2024-10-02 12:03:50

此函数不是线程安全的,因为它会导致未定义的行为。当您尝试获取写锁时,您已经持有读锁。来自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,则无需担心返回时释放持有的锁]

This function is not thread-safe because it results in undefined behavior. When you attempt to obtain the write lock, you already hold a read lock. From the documentation for pthread_rwlock_wrlock:

Results are undefined if the calling thread holds the read-write lock (whether a read or write lock) at the time the call [to pthread_rwlock_wrlock] is made.

This solution is also not exception-safe. If an exception is thrown while the lock is held, the lock will not be released and your application will undoubtedly deadlock. You should use a C++ threading library (Boost.Thread, OpenThreads, just::thread, or something similar) that provides a C++-oriented design supporting things like scoped_lock (or lock_guard).

As for making the algorithm correct, you need something along the lines of:

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

[If you use some sort of lock_guard, you don't need to worry about releasing held locks when you return]

能否归途做我良人 2024-10-02 12:03:50

没有正确实施。不能在保持读锁的同时获取写锁,以免发生死锁。来自我的 Linux 机器上 pthread_rwlock_wrlock 的手册页:

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

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

还有异常安全的常见问题......考虑范围保护或 try/catch 块。

Not properly implemented. You can't take the write lock while still holding the read lock, for fear of deadlock. From the man page for pthread_rwlock_wrlock on my Linux box:

The pthread_rwlock_wrlock() function shall apply a write lock to the
read-write lock referenced by rwlock. The calling thread acquires the
write lock if no other thread (reader or writer) holds the read-write
lock rwlock. Otherwise, the thread shall block until it can acquire
the lock. The calling thread may deadlock if at the time the call is
made it holds the read-write lock (whether a read or write lock).

Further, you should check the return value of the calls... for example, there's an implementation-defined limit to the number of simultaneous readers.

There are also the usual issues with exception safety... consider a scope guard or try/catch block.

盗心人 2024-10-02 12:03:50

一旦获得写锁,您可以修复它并再次查找该值。这应该足以解决您所描述的问题。像这样的东西:

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;
}

You might fix it looking for the value again once you have acquired the write lock. That should be enough to fix the problem you are describing. Something like:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文