寻找对我的读者/作者实施的批评

时间:2014-02-03 21:31:00

标签: 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 个答案:

答案 0 :(得分:1)

的反馈:

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 。我我在读/写逻辑中看到了一个错误:

WR

在步骤1中,线程1具有写锁定。

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

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

在步骤4中,线程1通过将okToWrite递减为0来释放写锁定,并发出信号AW == 0

在步骤5中,线程2,尽管没有被发信号通知,被唤醒不合逻辑,注意到,WR,并通过设置AR为0,并抓住读锁{ {1}}到1。

在步骤6中,线程3接收信号,注意AW == 0,并通过将WW设置为0并将AW设置为1来获取写锁定。

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

6。班级ReadersWriters有两个功能:

  1. 它实现了一个读/写互斥。
  2. 它实现了线程执行的任务。
  3. 更好的设计将利用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名称是因为当前在C ++ 1y中的std::shared_lock<T>类型(我们希望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互斥体类型,与成员函数非常精心挑选的名字,你会得到与STD-定义锁的互操作性,condition_variable_any,并锁定算法。

    有关此框架的更深入的理由,请参阅N2406