c++ - 寻找对我的读者/作家实现的批评

标签 c++ multithreading algorithm c++11 locking

<分区>

我在 C++11 中实现了读者/作者问题……我想知道它出了什么问题,因为这些事情我自己很难预测。

  • 共享数据库:
    • 当没有写者时,读者可以访问数据库
    • 当没有读者或作者时,作者可以访问数据库
    • 一次只有一个线程操作状态变量

该示例有 3 个读者和 1 个作者,但也使用 2 个或更多作者....

代码:

class ReadersWriters {
private:
    int AR; // number of active readers
    int WR; // number of waiting readers
    int AW; // number of active writers
    int WW; // number of waiting writers
    mutex lock;
    mutex m;
    condition_variable okToRead;
    condition_variable okToWrite;

    int data_base_variable;

public:
    ReadersWriters() : AR(0), WR(0), AW(0), WW(0), data_base_variable(0) {}

    void read_lock() {
        unique_lock<mutex> l(lock);

        WR++; // no writers exist
        // is it safe to read?
        okToRead.wait(l, [this](){ return WW == 0; });
        okToRead.wait(l, [this](){ return AW == 0; });
        WR--; // no longer waiting

        AR++;  // now we are active
    }

    void read_unlock() {
        unique_lock<mutex> l(lock);

        AR--; // no longer active

        if (AR == 0 && WW > 0) { // no other active readers
            okToWrite.notify_one(); // wake up one writer
        }
    }

    void write_lock() {
        unique_lock<mutex> l(lock);

        WW++; // no active user exist
        // is it safe to write?
        okToWrite.wait(l, [this](){ return AR == 0; });
        okToWrite.wait(l, [this](){ return AW == 0; });
        WW--; // no longer waiting

        AW++; // no we are active
    }
    void write_unlock() {
        unique_lock<mutex> l(lock);

        AW--; // no longer active

        if (WW > 0) { // give priority to writers
            okToWrite.notify_one(); // wake up one writer
        }
        else if (WR > 0) { // otherwize, wake reader
            okToRead.notify_all(); // wake all readers
        }
    }

    void data_base_thread_write(unsigned int thread_id) {
        for (int i = 0; i < 10; i++) {
            write_lock();

            data_base_variable++;
            m.lock();
            cout << "data_base_thread: " << thread_id << "...write: " << data_base_variable << endl;
            m.unlock();
            write_unlock();

            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    void data_base_thread_read(unsigned int thread_id) {
        for (int i = 0; i < 10; i++) {
            read_lock();

            m.lock();
            cout << "data_base_thread: " << thread_id << "...read: " << data_base_variable << endl;
            m.unlock();

            read_unlock();

            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
};

int main() {
    // your code goes here
    ReadersWriters rw;

    thread w1(&ReadersWriters::data_base_thread_write, &rw, 0);
    thread r1(&ReadersWriters::data_base_thread_read, &rw, 1);
    thread r2(&ReadersWriters::data_base_thread_read, &rw, 2);
    thread r3(&ReadersWriters::data_base_thread_read, &rw, 3);

    w1.join();
    r1.join();
    r2.join();
    r3.join();

    cout << "\nThreads successfully completed..." << endl;

    return 0;
}

最佳答案

反馈:

1 .它缺少所有必要的#includes。

2 .它假设一个 using namespace std ,这在声明中是一种糟糕的风格,因为它会污染所有使用命名空间 std 的客户端。

3 .锁的释放不是异常安全的:

write_lock();

data_base_variable++;
m.lock();
cout << "data_base_thread: " << thread_id << "...write: " << data_base_variable << endl;
m.unlock();           // leaked if an exception is thrown after m.lock()
write_unlock();       // leaked if an exception is thrown after write_lock()

4 . m.lock() cout 的包装在data_base_thread_write真的没必要,因为write_lock()应该已经提供独占访问权限。但是我知道这只是一个演示。

5 .我认为我在读/写逻辑中发现了一个错误:

step   1     2     3    4     5    6
WR     0     1     1    1     0    0
AR     0     0     0    0     1    1
WW     0     0     1    1     1    0
AW     1     1     1    0     0    1

第1步,线程1有写锁。

在步骤 2 中,线程 2 尝试获取读锁,递增 WR ,并在第二个 okToRead 上阻塞, 等待 AW == 0 .

在第 3 步中,线程 3 尝试获取写锁,递增 WW ,并在第二个 okToWrite 上阻塞, 等待 AW == 0 .

第4步,线程1释放,写锁通过递减AW到 0,并发出信号 okToWrite .

在第 5 步中,线程 2 尽管未收到信号,但虚假地被唤醒,注意到 AW == 0 , 并通过设置 WR 获取读锁到 0 和 AR到 1。

在第 6 步中,线程 3 接收到信号,注意到 AW == 0 , 并通过设置 WW 获取写锁到 0 和 AW到 1。

在步骤 6 中,线程 2 拥有读锁,线程 3 拥有写锁(同时)。

6 .类(class)ReadersWriters有两个功能:

  1. 它实现了读/写互斥体。
  2. 它实现线程执行的任务。

更好的设计将利用 C++11 中建立的互斥锁/锁框架:

创建一个 ReaderWriter与成员互斥:

// unique ownership
void lock();      // write_lock
void unlock();    // write_unlock
// shared ownership
lock_shared();    // read_lock
unlock_shared();  // read_unlock

前两个名字,lockunlock故意与 C++11 互斥类型使用的名称相同。只做这么多就可以让你做这样的事情:

std::lock_guard<ReaderWriter>  lk1(mut);
// ...
std::unique_lock<ReaderWriter> lk2(mut);
// ...
std::condition_variable_any cv;
cv.wait(lk2);  // wait using the write lock

如果你添加:

void try_lock();

那么你还可以:

std::lock(lk2, <any other std or non-std locks>);  // lock multiple locks

lock_sharedunlock_shared选择名称是因为 std::shared_lock<T>当前在 C++1y(我们希望 y 是 4)工作草案中键入。它记录在 N3659 中. 然后你可以这样说:

std::shared_lock<ReaderWriter> lk3(mut);   // read_lock
std::condition_variable_any cv;
cv.wait(lk3);  // wait using the read lock

即通过创建一个独立的 ReaderWriter mutex 类型,为成员函数精心选择名称,您可以获得与标准定义锁的互操作性,condition_variable_any , 和锁定算法。

参见 N2406以获得该框架的更深入的基本原理。

关于c++ - 寻找对我的读者/作家实现的批评,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21538209/

相关文章:

c++ - 找到最接近的斐波那契数

c++ - (C++) "No operator ">>"matches these operands"枚举

algorithm - 从列表的映射中输出在每个其他元素列表中不相交的元素集的映射

algorithm - 对于某个问题,如果我有一个 O(f1(m,n)) 算法和一个 O(f2(m,n)) 算法,我可以有一个 O(min(f1(m,n),f2(m, n)))算法?

c# - 什么相当于C#中的map of maps

c++ - 如何在我的 C++ 源代码中查找(并替换)所有旧的 C 样式数据类型转换?

c++ - 为什么竞赛条件的输出不是随机的?

java - Tomcat 无法停止 webapp 中的线程

Java:异步并发写入磁盘

c++ - 在 C++ 中从其伪代码实现算法