C++11多读单写线程互斥锁

31

我有一个应用程序,其中一些STL容器在3个线程中被读取,并且在2个线程中被写入。我知道有TBB用于多线程容器,但这不是我的应用程序的选项。

因此,我想使用std::mutex和我的双手使程序支持多线程。这是我所做的简单版本:

int readers = 0;
std::mutex write;

// One write, no reads.
void write_fun()
{
    write.lock();// We lock the resource
    while(readers > 0){}// We wait till everyone finishes read.
    // DO WRITE
    write.unlock();// Release
}

// Multiple reads, no write
void read_fun()
{
    // We wait if it is being written.
    while(!write.try_lock()){}
    write.unlock();

    readers++;
    // do read
    readers--;
}

这是在C++11中正确的做法吗?

2个回答

49

非常接近,有几点需要注意,在C++中为了异常安全和可读性,我认为最好使用RAII锁。你真正需要的是一个像boost中的shared_mutex或即将在C++14中引入的一样。

std::shared_mutex write; //use boost's or c++14 

// One write, no reads.
void write_fun()
{
    std::lock_guard<std::shared_mutex> lock(write);
    // DO WRITE
}

// Multiple reads, no write
void read_fun()
{
    std::shared_lock<std::shared_mutex> lock(write);
    // do read
}

如果您不想使用boost,@howardhinmant很善良地提供了一个参考实现的链接。


1
你在 lock_guardshared_lock 上使用了错误的互斥类型(应该是 std::shared_mutex)。此外,这里有一个参考实现: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp。 - Howard Hinnant
@HowardHinnant 已经修复了,我今天一定是状态不佳,把一切都搞砸了,我也会在答案中添加参考实现。 - aaronman
+1 感谢您的出色回答。我想这就是我不能再将这个巨大的依赖项添加到我的项目中的时候了。 - VSZM
@VSZM,你看到底部的参考实现链接了吗?我认为它并不是很大(你不需要添加boost)。 - aaronman
是的,我看到了,但其实很早以前我就应该加入boost了,这样我就可以避免许多其他的实现。不再拖延了。而且结果证明,只需要在NuGet上点击几下就搞定了。 - VSZM
3
我很抱歉,std::shared_mutex 是(将会)在 C++17 中出现,而不是 C++14。 - Messa

8

这是安全的,但仍然可能不公平或者性能不佳:

std::atomic<int> readers;
std::mutex write;

// One write, no reads.
void write_fun()
{
    write.lock();// We lock the resource
    while(readers > 0){}// We wait till everyone finishes read.
    // DO WRITE
    write.unlock();// Release
}

// Multiple reads, no write
void read_fun()
{
    // We wait if it is being written.
    write.lock();
    readers++;
    write.unlock();

    // do read
    readers--;
}

使用条件变量的解决方案可以避免读者数量降为0时的忙等待,具体实现方法请留给读者自行思考。

1
我认为这个算法容易导致写者饥饿。如果读者足够频繁地返回,写者将永远不会看到“读者”降至零。我见过的最好的算法是由Alexander Terekhov设计的2门系统,并在此实现中使用:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp 它禁止了读者和写者的饥饿。 - Howard Hinnant
1
@HowardHinnant 我不会将此作为一个健壮的通用解决方案来进行辩护,意图是尽可能地做出最小的更改以确保 OP 的方法的安全性。写者饥饿不是问题,然而一旦写者锁定互斥量,新读者将无法获取访问权限。 - Casey
谢谢,现在我看到了代码中的错误。我非常喜欢这个答案。可惜我只能选择一个。 - VSZM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接