我在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;
}
答案 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
有两个功能:
更好的设计将利用C ++ 11中建立的互斥锁/锁框架:
使用成员创建ReaderWriter
互斥锁:
// unique ownership
void lock(); // write_lock
void unlock(); // write_unlock
// shared ownership
lock_shared(); // read_lock
unlock_shared(); // read_unlock
前两个名称lock
和unlock
的名称与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_shared
和unlock_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。