18

我正在检查 boost 库(1.45 版)的读/写锁。当我对其进行测试时,似乎 shared_ptr 更喜欢我的读者线程,即当我的作者试图为其操作获取锁时,它并没有阻止任何后续读取的发生。

是否有可能改变这种行为?

using namespace std;
using namespace boost;

mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;

class WorkerKiller
{
public:   

    void operator()()  
    {
        upgrade_lock<shared_mutex> lock(workerAccess); 
        upgrade_to_unique_lock<shared_mutex> uniqueLock(lock);

        cout << "Grabbed exclusive lock, killing system" << endl;
        sleep(2);
        shouldIWork = false;
        cout << "KILLING ALL WORK" << endl;  
    }  

private:  
};

class Worker
{  
public:   

    Worker()
    {  
    }  

    void operator()()  
    {
        shared_lock<shared_mutex> lock(workerAccess); 

        if (!shouldIWork) {
            outLock.lock();
            cout << "Workers are on strike.  This worker refuses to work" << endl;
            outLock.unlock();
        } else {
            sleep(1);

            outLock.lock(); 
            cout << "Worked finished her work" << endl;
            outLock.unlock(); 
        }
    }  
};  

int main(int argc, char* argv[])  
{  
    Worker w1;
    Worker w2;
    Worker w3;
    Worker w4;
    WorkerKiller wk;

    boost::thread workerThread1(w1);
    boost::thread workerThread2(w2);

    boost::thread workerKillerThread(wk);

    boost::thread workerThread3(w3);
    boost::thread workerThread4(w4);

    workerThread1.join();
    workerThread2.join();
    workerKillerThread.join();
    workerThread3.join();

    return 0;
}

这是每次的输出:

工作完成了她的工作 工作
完成了

的 工作


我的要求

如果作者试图获取独占锁,我希望所有之前的读取操作都完成。然后将所有后续的读操作阻塞。

4

2 回答 2

33

我对这个问题有点晚了,但我相信我有一些相关的信息。

boost 库所基于的 C++ 委员会的提案shared_mutex故意没有指定一个 API 来给予读者或作者优先权。这是因为 Alexander Terekhov 大约十年前提出了一个完全公平的算法。它让操作系统决定下一个获取互斥锁的线程是读取器还是写入器,操作系统完全不知道下一个线程是读取器还是写入器。

由于该算法,不再需要指定阅读器或作者是首选。据我所知,boost 库现在(boost 1.52)是用这种公平的算法实现的。

Terekhov 算法包括让读/写互斥体由两个门组成:gate1 和 gate2。每次只有一个线程可以通过每个门。门可以用一个互斥锁和两个条件变量来实现。

读者和作者都试图通过gate1。为了通过gate1,编写器线程当前不在gate1内部必须是真的。如果有,则试图通过 gate1 的线程会阻塞。

一旦读取器线程通过gate1,它就读取了互斥锁的所有权。

当写入线程通过gate1 时,它还必须通过gate2 才能获得互斥锁的写入所有权。直到 gate1 内的读卡器数量降为零,它才能通过 gate2。

这是一个公平的算法,因为当 gate1 内部只有 0 个或多个读取器时,由操作系统决定下一个进入 gate1 的线程是读取器还是写入器。只有在通过 gate1 之后,写入者才会被“优先化”,因此接下来将获得互斥锁的所有权。

我使用了针对最终shared_timed_mutex在 C++14 中的示例实现编译的示例(对您的示例稍作修改)。下面的代码调用它shared_mutex,这是它提出时的名称。

我得到以下输出(都具有相同的可执行文件):

有时:

Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work

而有时:

Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work
Workers are on strike.  This worker refuses to work

而有时:

Worked finished her work
Worked finished her work
Worked finished her work
Worked finished her work
Grabbed exclusive lock, killing system
KILLING ALL WORK

我相信理论上也应该有可能获得其他输出,尽管我没有通过实验证实这一点。

为了充分披露,这里是我执行的确切代码:

#include "../mutexes/shared_mutex"
#include <thread>
#include <chrono>
#include <iostream>

using namespace std;
using namespace ting;

mutex outLock;
shared_mutex workerAccess;
bool shouldIWork = true;

class WorkerKiller
{
public:   

    void operator()()  
    {
        unique_lock<shared_mutex> lock(workerAccess); 

        cout << "Grabbed exclusive lock, killing system" << endl;
        this_thread::sleep_for(chrono::seconds(2));
        shouldIWork = false;
        cout << "KILLING ALL WORK" << endl;  
    }  

private:  
};

class Worker
{  
public:   

    Worker()
    {  
    }  

    void operator()()  
    {
        shared_lock<shared_mutex> lock(workerAccess); 

        if (!shouldIWork) {
            lock_guard<mutex> _(outLock);
            cout << "Workers are on strike.  This worker refuses to work" << endl;
        } else {
            this_thread::sleep_for(chrono::seconds(1));

            lock_guard<mutex> _(outLock);
            cout << "Worked finished her work" << endl;
        }
    }  
};  

int main()  
{  
    Worker w1;
    Worker w2;
    Worker w3;
    Worker w4;
    WorkerKiller wk;

    thread workerThread1(w1);
    thread workerThread2(w2);

    thread workerKillerThread(wk);

    thread workerThread3(w3);
    thread workerThread4(w4);

    workerThread1.join();
    workerThread2.join();
    workerKillerThread.join();
    workerThread3.join();
    workerThread4.join();

    return 0;
}
于 2012-11-19T00:06:17.360 回答
1

谷歌搜索“提升共享锁饥饿”出现了这个链接:

看起来“升级”可能是关键。也可以看看:

于 2012-08-22T22:47:40.733 回答