【问题标题】:Boost Mutex Scoped Lock增强互斥锁
【发布时间】:2013-05-28 18:45:34
【问题描述】:

我正在阅读 drdobbs.com 上的 Boost Mutex 教程,并找到了这段代码:

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex io_mutex;

void count(int id)
{
  for (int i = 0; i < 10; ++i)
  {
    boost::mutex::scoped_lock
      lock(io_mutex);
    std::cout << id << ": " <<
      i << std::endl;
  }
}

int main(int argc, char* argv[])
{
  boost::thread thrd1(
    boost::bind(&count, 1));
  boost::thread thrd2(
    boost::bind(&count, 2));
  thrd1.join();
  thrd2.join();
  return 0;
}

现在我明白了 Mutex 的意义在于防止两个线程同时访问同一个资源,但我没有看到 io_mutex 和 std::cout 之间的相关性。这段代码是否只是锁定范围内的所有内容,直到范围完成?

【问题讨论】:

  • 是的,scoped_lock 锁定互斥体,直到退出作用域。它有一个相当直观的名称。
  • @juanchopanza 我理解这部分,scoped_lock 非常有意义。我没有得到的是互斥锁与程序中其他任何东西之间的关系。它是否与程序中的执行顺序更相关,而与实际内存的关系更少?抱歉,如果这是一个菜鸟问题,但过去一周左右我一直在尝试学习互斥锁,但没有得到它。
  • @jlw2387:正如我在回答中所写,std::cout 是一个全局对象,因此您可以将其视为共享资源。从不同线程并发访问共享资源需要同步。这就是scoped_lock 正在做的事情

标签: c++ multithreading boost mutex scoped-lock


【解决方案1】:

现在我明白了 Mutex 的意义在于防止两个线程同时访问同一个资源,但我没有看到 io_mutex 和 std::cout 之间的相关性。

std::cout 是一个全局对象,因此您可以将其视为共享资源。如果您从多个线程同时访问它,这些访问必须以某种方式同步,以避免数据竞争和未定义的行为。

考虑到以下几点,您可能会更容易注意到并发访问:

std::cout << x

实际上相当于:

::operator << (std::cout, x)

这意味着您正在调用一个对std::cout 对象进行操作的函数,并且您同时从不同的线程执行此操作。 std::cout 必须以某种方式受到保护。但这并不是scoped_lock 存在的唯一原因(继续阅读)。

此代码是否只是锁定范围内的所有内容,直到范围完成?

是的,它会锁定io_mutex,直到锁定对象本身超出范围(作为典型的 RAII 包装器),这发生在 for 循环的每次迭代结束时。

为什么需要它?好吧,虽然在 C++11 中单独插入 cout 保证是线程安全的,但随后的单独插入可能会在多个线程输出某些内容时交错。

请记住,通过operator &lt;&lt; 的每次插入都是一个单独的函数调用,就像您正在做的那样:

std::cout << id;
std::cout << ": ";
std::cout << i;
std::cout << endl;

operator &lt;&lt; 返回流对象这一事实允许您将上述函数调用链接到一个表达式中(就像您在程序中所做的那样),但您仍然有几个单独的函数调用这一事实。

现在看上面的sn-p,更明显的是这个作用域锁的目的是为了保证表单的每条消息:

<id> ": " <index> <endl>

在不与其他消息的部分交错的情况下打印。

此外,在 C++03 中(cout 的插入保证是线程安全的),锁将保护 cout 对象本身不被并发访问。

【讨论】:

  • 我不确定您是否明确表示互斥锁锁定了代码的关键区域——执行路径,并且没有任何资源或对象。关于 pre-C++11 中&gt;&gt; 的线程安全:这取决于实现。有些是线程安全的。我很惊讶你说std::cout&lt;&lt; i 在 C++11 中是线程安全的。我看不出有任何理由这样做。 (我也找不到标准在哪里保证它。但我也不知道在哪里看。)
  • @JamesKanze:在 §27.4.1/4 中有规定。
  • 我想知道为什么。它不会给你买任何东西(正如你非常正确地指出的那样),它违反了图书馆其他地方使用的一般原则。 (一种更有用的方法是提供流的互斥体成员,这样即使彼此不了解的库也可以就要使用的互斥体达成一致。)
  • “互斥锁锁定了代码的关键区域——执行路径,而不是任何资源或对象”——错误。 boost::mutex::scoped_lock 构造函数(RAII 的 II 部分)锁定传递给它的 boost::mutex 对象(RAII 的 RA 部分)。同一个互斥锁可以锁定任意数量的代码段。例如,OP 的程序可以在多个地方写入 cout,每个地方都受相同的互斥锁保护。 “我想知道为什么。” -- 因此,在多个线程中非锁定使用 cout 不会导致未定义的行为。你甚至读过这个部分吗? “正如你非常正确地指出的那样”——他没有这样做。
【解决方案2】:

互斥锁与程序中的任何其他内容无关 (条件变量除外),至少在更高的水平上。 互斥体有两个作用:控制程序流,防止 多个线程执行相同的代码块 同时地。它还确保内存同步。这 这里的重要问题是互斥锁与 资源,并且不要阻止两个线程访问同一个 同时资源。互斥锁定义了一个临界区 代码,一次只能由一个线程输入。如果 对特定资源的所有使用都是在关键的情况下完成的 由同一互斥锁控制的部分,则资源为 受到互斥锁的有效保护。但关系是 由编码器建立,通过确保所有使用确实需要 放在关键部分。

【讨论】:

  • queue&lt;int&gt;q_; q_.push(1); boost::mutex q_mutex; void func1() { { boost::mutex::scoped_lock start_stop_lock(q_mutex_); while(true) q_.push(1); } } int main() { boost::thread t1(func1()); //print top element std::cout &lt;&lt; q_.top() &lt;&lt; std::endl; } 考虑上述情况。你是说 main 函数中定义的 cout 将能够访问 q_ 并打印 q_.top() 的值?
猜你喜欢
  • 1970-01-01
  • 2012-11-28
  • 2018-05-23
  • 1970-01-01
  • 2010-09-16
  • 2010-12-17
  • 2023-03-03
  • 2012-06-05
  • 1970-01-01
相关资源
最近更新 更多