【问题标题】:mutex C++ good usagemutex C++ 很好的用法
【发布时间】:2015-04-10 10:02:37
【问题描述】:

我在使用互斥锁时遇到了一些问题,请考虑这个例子:

boost::mutex m;


void thread1_unstack(std::stack<std::string>& msg) {

  while (true) {
    if (msg.empty()) continue;

    m.lock();
    std::string msg_string = msg.top();
    msg.pop();
    std::cout << msg_string << std::endl;  
    m.unlock();
  }
}

void thread2_stack(std::stack& msg) {
   while (1) {
      msg.push("very long message");  
   }
}

void wait_for_finish(std::stack& msg) {
   while (!msg.empty()) sleep(1);
}

int main() {
   std::stack<std::string> msg;

   boost::thread t1 = boost::thread(boost::bind(&thread1_unstack, boost::ref(msg));

   boost::thread t2 = boost::thread(boost::bind(&thread2_stack, boost::ref(msg));

   wait_for_finish(msg);

   t1.stop();
   t2.stop();
}

所以问题在于函数wait_for_finish。该函数在调用 msg.pop() 时检测堆栈为空,因此线程会在之后立即停止,有时,消息 (std::cout) 不会完全打印在屏幕上。

所以我想为这 3 行“锁定”msg 变量:

    std::string msg_string = msg.top();
    msg.pop();
    std::cout << msg_string << std::endl;

这样,wait_for_finish 在 std::cout 期间不会检测到堆栈为空。

我试图锁定一个 boost::mutex 并在最后解锁它,但没有任何改变。

所以不知道怎么解决

【问题讨论】:

  • 你为什么不向我们展示无效的代码?
  • 太长,保密协议,但这个例子很好地描述了我的问题
  • 好吧,你的代码中有很多错误。例如,thread1 立即调用msg.empty 而不持有互斥体,线程2 调用push 而不持有互斥体。所以你的代码到处都是坏的。您不能在一个线程中访问 stack,而另一个线程可能正在修改它。
  • 我添加了互斥锁。所以就我而言,wait_for_finish、thread1_unstack 和 mutex 是类的成员函数。 thread2_stack 是一个独立的函数
  • 您需要使用互斥锁保护对msg每个访问。否则,您可能有一个线程访问堆栈,而另一个线程正在修改它,这是不允许的。如果这是一个重要程序中的现实世界问题,值得把事情做好,您可能需要创建(或找到)一个线程安全的可等待堆栈。

标签: c++ multithreading boost mutex


【解决方案1】:

您必须使用互斥锁保护所有访问:

boost::mutex m;


void thread1_unstack(std::stack<std::string>& msg) {

  while (true) {

    m.lock();
    bool msgEmpty = msg.empty();
    m.unlock();

    if (msgEmpty) continue;

    m.lock();
    std::string msg_string = msg.top();
    msg.pop();
    std::cout << msg_string << std::endl;  
    m.unlock();
  }
}

void thread2_stack(std::stack& msg) {
   while (1) {
      m.lock();
      msg.push("very long message");  
      m.unlock();
   }
}

void wait_for_finish(std::stack& msg) {

    while(true) {

        m.lock();
        bool msgEmpty = msg.empty();
        m.unlock();

        if(msgEmpty) break;
        sleep(1);
    }
}

int main() {
   std::stack<std::string> msg;

   boost::thread t1 = boost::thread(boost::bind(&thread1_unstack, boost::ref(msg));

   boost::thread t2 = boost::thread(boost::bind(&thread2_stack, boost::ref(msg));

   wait_for_finish(msg);

   t1.stop();
   t2.stop();
}

【讨论】:

    【解决方案2】:

    我们讨论中的评论表明您正在使用某种线程安全的可等待容器。您需要通过它的合作来停止线程,以确保它有时间完成它应该对最后一个对象执行的任何工作。我会建议这种方法:

    1. 有一个stop 标志,它要么是原子的,要么是受互斥体保护的。它应该开始清除。

    2. 当线程从容器中获取对象时,在打印它之前,它会检查stop 标志。如果设置了标志,则线程终止。

    3. 当你想停止线程时,设置stop标志,添加一个虚拟对象到容器中,然后加入线程。

    虚拟对象解除对线程的阻塞,并且它不会被打印,因为设置了stop 标志的线程不会打印。通过join线程,您可以确保它在终止之前完成了它必须完成的所有工作。

    您也可以使用“死亡对象”的方法。例如,假设您的代码不可能将空字符串排队——您可以将空字符串用作“死亡对象”。它的工作原理是这样的:

    1. 当你从容器中获取一个对象时,检查它是否是死亡对象。如果是,则终止。

    2. 要使线程终止,将死亡对象排队,然后加入线程。

    这具有相同的行为。对死亡对象进行排队会解除对线程的阻塞(因为容器不再是空的,并且只有在空的情况下才会阻塞)并保证线程在完成时将终止(因为线程一出队就会检查)。加入线程可确保它在死亡对象之前已完全处理完所有对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      • 2011-01-17
      相关资源
      最近更新 更多