【问题标题】:Failure to create a monitor with mutex and condition_variable in C++11在 C++11 中无法使用互斥锁和 condition_variable 创建监视器
【发布时间】:2020-11-19 03:31:20
【问题描述】:

我正在尝试创建一个监视器类来获取数据总和[]。在课堂上,我使用监视器来做阅读/写作工作。然而,condition_variable、unique_lockmutex 让我感到困惑。当数据大小不超过 56 时,我的代码可以正常工作,但如果数据大小较大,代码会失败,并且在 lldb 中调试时condition_variable.wait(cond_lock) 无法工作。

错误:输入 std::__1::system_error: unique_lock::unlock: 未锁定: 不允许操作

不能帮助我理解问题。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

const int datasize = 58;//*****datasize cannot be bigger than 56*****//
int *data = new int[datasize];

void data_init(){
    for (int i = 0; i < datasize; ++i) {
        data[i] = random() % datasize + 1;
    }
    int sum = 0;
    for (int i = 0; i < datasize; ++i) {
        sum += data[i];
    }
    std::cout << "true answer: " << sum << std::endl;
}
class monitor{
public:
    std::mutex the_mutex;//the mutex to lock the function
    std::unique_lock<std::mutex> cond_mutex;//trying to use this for condition_variable
    std::condition_variable read_to_go, write_to_go;
    int active_reader, active_writer, waiting_reader, waiting_writer;
    bool write_flag;
    void getTask(int Rank, int& task_one, int& task_two, int& second_rank);//**reader**//
    void putResult(int Rank, int the_answer, int next_Rank);//**writer**//
    explicit monitor(){
        write_flag = true;
        active_reader = active_writer = waiting_reader = waiting_writer = 0;
    }
private:
    inline void startRead();
    inline void endRead();
    inline void startWrite();
    inline void endWrite();
};

monitor imonitor;

inline void monitor::startRead() {
    the_mutex.lock();//lock the function code
    cond_mutex.lock();//updated 1st
    while((active_writer + active_reader) > 0){//if there are working reader and writer
        waiting_reader++;//add one 
        read_to_go.wait(cond_mutex);//wait the thread
        /*****when debugging with lldb, error appears here*****/
        waiting_reader--;//one less reader waiting when notified
    }
    active_reader++;//one more working reader
    the_mutex.unlock();
}

inline void monitor::endRead() {
    the_mutex.lock();
    active_reader--;//one less reader working
    if(active_reader == 0 && waiting_writer > 0){//if there is not any reader working and there are some writer waiting
        write_to_go.notify_one();//notify one writer
    }//else get out directly
    the_mutex.unlock();
}

inline void monitor::startWrite() {
    the_mutex.lock();
    cond_mutex.lock();//updated 1st
    while((active_writer + active_reader) > 0){//if any reader or writer is working
        waiting_writer++;//one more writer waiting
        write_to_go.wait(cond_mutex);//block this thread
        waiting_writer--;//when notfied, the number of waiting writer become less
    }
    active_writer++;//one more active writer
    the_mutex.unlock();//updated 1st
}

inline void monitor::endWrite() {//write is over
    the_mutex.lock();
    active_writer--;//one less writer working
    if(waiting_writer > 0){//if any writer waiting
        write_to_go.notify_one();//notify one of them
    }
    else if(waiting_reader > 0){//if any reader waiting
        read_to_go.notify_all();//notify all of them
    }
    the_mutex.unlock();
}

void monitor::getTask(int Rank, int &task_one, int &task_two, int &second_rank) {
    startRead();
    task_one = data[Rank];
    while(Rank < (datasize - 1) && data[++Rank] == 0);
    task_two = data[Rank];
    second_rank = Rank;
    //std::cout << "the second Rank is " << Rank << std::endl;
    endRead();
}

void monitor::putResult(int Rank, int the_answer, int next_Rank) {
    startWrite();
    data[Rank] = the_answer;
    data[next_Rank] = 0;
    endWrite();
}

void reducer(int Rank){
    //std::cout << "a reducer begins" << Rank << std::endl;
    do {
        int myTask1, myTask2, secondRank;
        imonitor.getTask(Rank, myTask1, myTask2, secondRank);
        if(myTask2 == 0) return;
        //std::cout << "the second value Rank: " << secondRank << std::endl;
        int answer = myTask1 + myTask2;
        imonitor.putResult(Rank, answer, secondRank);
    }while (true);
}

int main() {
    std::cout << "Hello, World!" << std::endl;
    data_init();

    std::thread Reduce1(reducer, 0);
    std::thread Reduce2(reducer, datasize/2);
    /*std::thread Reduce3(reducer, 4);
    std::thread Reduce4(reducer, 6);
    std::thread Reduce5(reducer, 8);
    std::thread Reduce6(reducer, 10);
    std::thread Reduce7(reducer, 12);
    std::thread Reduce8(reducer, 14);*/

    Reduce1.join(); //std::cout << "A reducer in" <<std::endl;
    Reduce2.join();
    /*Reduce3.join();
    Reduce4.join();
    Reduce5.join();
    Reduce6.join();
    Reduce7.join();
    Reduce8.join();*/

    std::cout << data[0] << std::endl;
    return 0;
}

以前我的目标是使用 8 个线程,但现在代码只能使用一个线程。一些用于调试的cout 留在代码中。感谢您的任何帮助! 第一次更新:我在startWrite()startRead()the_mutex.lock() 之后添加cond_mutex.lock()startWrite最后一行关于cond_mutex.unlock()的错误已修复,替换为the_mutex.unlock()。但是,问题并没有解决。

【问题讨论】:

  • startWrite() 中,您锁定了the_mutex,但您解锁了cond_mutex
  • startWrite()的第一行,我锁定the_mutex,在最后一行解锁。你的意思是我不解锁read_to_go.wait(cond_mutex)中的the_mutex
  • 你没有在startWrite()的最后一行解锁the_mutex,而是解锁cond_mutex
  • .wait()之前应该锁定unique_lock的地方
  • 好的,我找到了。非常感谢你。现在,我相信我的眼睛需要放松。

标签: c++ c++11 mutex monitor condition-variable


【解决方案1】:

好的,伙计们。感谢你们的 cmets,我很受启发来解决这个问题。

  1. 一开始,我在monitor类的声明中使用了std::unique_lock&lt;std::mutex&gt; cond_mutex;,意思是调用了unique_lock的默认初始化器。

    class monitor{ public: std::mutex the_mutex, assist_lock;/***NOTE HERE***/ std::unique_lock&lt;std::mutex&gt; cond_mutex;/***NOTE HERE***/ std::condition_variable read_to_go, write_to_go; int active_reader, active_writer, waiting_reader, waiting_writer; ...... };

  2. 让我们检查标题 __mutex_base,其中定义了 mutexunique_lock。(从第 104 行开始)

    `模板 类_LIBCPP_TEMPLATE_VIS unique_lock { 上市: typedef _Mutex mutex_type;

    私人: mutex_type* _m; bool _拥有;

    公开: _LIBCPP_INLINE_VISIBILITY unique_lock() _NOEXCEPT : _m(nullptr), _owns(false) {}/第一和默认 / _LIBCPP_INLINE_VISIBILITY 显式 unique_lock(mutex_type& __m) : _m(_VSTD::addressof(__m)), _拥有(true) {_m->lock();}/ 第二和拥有一个对象/ …… };`

    显然,第一个初始化器被使用而不是第二个。 __m_nullptr

  3. &lt;condition_variable&gt;.wait(cond_mutex);调用lock()时,我们会发现错误信息:"unique_lock::lock: references null mutex"(在标题__mutex_base,第207行)。

  4. &lt;unique_lock&gt;.unlock()被调用时,由于lock()不起作用bool__owns_false(第116&211行),我们会得到错误信息:@ 987654331@(第 257 行)。

为了解决这个问题,我们不能使用一个全覆盖的unique_lock cond_mutex,而是在每次我们想要使用startRead()endStart()对象the_mutex时初始化cond_mutex。这样,&lt;unique_lock&gt; cond_mutex(the_mutex) 被第二个初始化函数初始化,同时the_mutex 被锁定并且&lt;unique_lock&gt; cond_mutex 拥有the_mutex。在......的最后 startRead()endStart()cond_mutex.unlock() 解锁自己和 the_mutex,然后 cond_mutex 死并释放 the_mutex

class monitor{
public:
std::mutex the_mutex;
std::condition_variable read_to_go, write_to_go;
int active_reader, active_writer, waiting_reader, waiting_writer;
bool write_flag;
void getTask(int Rank, int& task_one, int& task_two, int& second_rank);//reader
void putResult(int Rank, int the_answer, int next_Rank);//writer
explicit monitor(){
    write_flag = true;
    active_reader = active_writer = waiting_reader = waiting_writer = 0;
}
private:
inline void startRead();
inline void endRead();
inline void startWrite();
inline void endWrite();
};

inline void monitor::startRead() {
std::unique_lock<std::mutex> cond_mutex(the_mutex);
while((active_writer + active_reader) > 0){
    waiting_reader++;
    read_to_go.wait(cond_mutex);
    waiting_reader--;
}
active_reader++;
cond_mutex.unlock();
}

inline void monitor::startWrite() {
std::unique_lock<std::mutex> cond_mutex(the_mutex);
while((active_writer + active_reader) > 0){
    waiting_writer++;
    write_to_go.wait(cond_mutex);
    waiting_writer--;
}
active_writer++;
cond_mutex.unlock();

}

解决办法就到这里了,只是把我原来的一部分代码替换成上面的代码。毕竟感谢所有给cmets的人。

【讨论】:

  • 停止手动解锁互斥锁。使用范围。使用等待的“lambda”重载而不是手动循环。 ++waiting_blah; cv.wait(lock, [&amp;]{ return !active_read &amp;&amp; !active_write; } ); --waiting_blah;
  • 我看到了你的回答,非常感谢。我是初学者,暂时不知道 lambda。 :)@Takk - 亚当 Nevraumont
【解决方案2】:

你好像在写这个:

class write_wins_mutex {
  std::condition_variable cv_read;
  std::condition_variable cv_write;
  std::mutex m;
  int readers=0;
  int writers=0;
  int waiting_writers=0;
  int waiting_readers=0;
  std::unique_lock<std::mutex> internal_lock(){ return std::unique_lock<std::mutex>( m );
public:
  void shared_lock() {
    auto l = internal_lock();
    ++waiting_readers;
    cv_read.wait(l, [&]{ return !readers && !writers && !waiting_writers; } );
    --waiting_readers;
    ++readers;
  }
  void lock() {
    auto l = internal_lock();
    ++waiting_writers;
    cv_write.wait(l, [&]{ return !readers && !writers; } );
    --waiting_writers;
    ++writers;
  }
private:
  void notify(){
    if (waiting_writers) {
      if(!readers) cv_write.notity_one();
    }
    else if (waiting_readers) cv_read.notify_all();
  }
public:
  void unlock_shared(){
    auto l = internal_lock();
    --readers;
    notify();
  }
  void unlock(){
    auto l = internal_lock();
    --writers;
    notify();
  }
};

可能有错别字;没有编译就写在手机上。

但这是一个与 shared_lock 和 unique_lock 兼容的互斥锁(没有 try lock (for),但这只是意味着这些方法不起作用)。

在这个模型中,读者是共享的,但如果有任何作者出现,它就会获得优先权(甚至可以让读者饿死)

如果你想要一些不那么有偏见的东西,直接使用共享互斥锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 2020-08-12
    相关资源
    最近更新 更多