提升,指向互斥锁的指针,会起作用吗? boost :: mutex和std :: vector,noncopyable问题

时间:2012-02-16 23:09:14

标签: c++ multithreading boost mutex boost-thread

以下代码会给我一个错误,因为boost :: mutex是不可复制的,而xyz.push_back()是一个复制构造函数。

class XYZ
{
    public:
        double x;
        boost::mutex x_mutex;
}

vector<XYZ> xyz;
xyz.push_back(XYZ());

所以我尝试过这样的事情,

class XYZ
{
    public:
        double x;
        boost::mutex * x_mutex;
}

vector<XYZ> xyz;
xyz.push_back(XYZ());

它没有错误,但问题是“互斥体实际上是否会按原样运行?”这是在类中封装互斥体,然后创建该类的向量的好方法吗?

感谢。

1 个答案:

答案 0 :(得分:6)

这里有两个问题:

  1. 是否可以正确创建互斥锁?

  2. 是否正确使用了互斥锁?

  3. 问题是1.答案是否定的。互斥锁指针未指向互斥锁。

    因此,您需要添加适当的构造函数。 既然你需要一个构造函数,你可能需要实现一个析构函数,复制构造函数和赋值运算符,如果你希望你的类行为正确的话。

    要么

    XYZ::XYZ() : x(0), x_mutex(new boost::mutex) {}
    XYZ::~XYZ() { delete x_mutex; }
    XYZ::XYZ( const XYZ & xyz ) : x(xyz.x), x_mutex( new boost::mutex ) {}
    XYZ& XYZ::operator=( const XYZ & xyz ) { x=xyz.x; }
    

    explicit XYZ::XYZ( boost::mutex * m ) : x(0), x_mutex(m) {}
    // Strictly speaking we dont need these as the default version does the right thing.
    XYZ::~XYZ() {}
    XYZ::XYZ( const XYZ & xyz ) : x(xyz.x), x_mutex( xyz.x_mutex ) {}
    XYZ& XYZ::operator=( const XYZ & xyz ) { x=xyz.x; x_mutex = xyz.x_mutex; }
    

    将是我所期待的。

    在第一种情况下,对象的每个实例和副本都有自己的互斥锁。在第二个对象中,每个对象与其副本共享一个互斥锁,但必须在实例之前创建互斥锁。

    还有第三种变体,其中互斥体可以由构造函数创建并与所有实例共享,但为此,您将shared_ptr保存到互斥锁而不是原始指针。

    class XYZ
    {
      public:
        double x;
        boost::shared_ptr<boost::mutex> x_mutex;
        XYZ() : x(0), x_mutex( new boost::mutex ) {}
        // Default versions of the other three do the right thing.
    };
    

    如果我们沿着这些路径走下去,我们最终会出现正确创建并初始化互斥锁的情况。

    现在对于棘手的部分“是否正确使用互斥锁?”。要回答这个问题,我们需要知道线程之间如何共享对象,互斥锁应该保护的共享数据是什么。

    如果在创建任何工作线程之前在主线程中创建了对象向量,并且每个对象实例都被工作线程修改(以便互斥锁真正保护x数据)那么第一个版本可能是正确的。在这种情况下,您的代码看起来更像是这样。

    //Main thread
    std::vector<XYZ> v;
    for(unsigned int i=0; i<10; ++i)
      v.push_back(XYZ());
    
    //Several worker threads like this
    j = rand()%10;
    v[j].x_mutex->lock();
    v[j].x+=1;
    v[j].x_mutex->unlock();
    

    如果x实际上是一个指针类型,并且它所指向的东西在线程之间共享,那么你的代码可能看起来像这样,并且要使用的代码的正确版本是2或3。

    //Main thread
    std::vector<XYZ> v;
    X * xa;
    boost::mutex xa_mutex;
    X * xb;
    boost::mutex xb_mutex;
    
    for(unsigned int i=0; i<5; ++i)
      v.push_back(XYZ(xa,xa_mutex));
    
    for(unsigned int i=0; i<5; ++i)
      v.push_back(XYZ(xb,xb_mutex));
    
    //Several worker threads like this
    j = rand()%10;
    v[j].x_mutex->lock(); 
    v[j].x->do_something();
    v[j].x_mutex->unlock();
    

    关键是每个共享资源都有一个互斥锁。

    请注意,从技术上讲,向量v在两种情况下都是共享资源,如果要在创建后更改,还应该由互斥锁保护。然而,这样的锁会(正确地)破坏所有并行性。所以让我们暂时忽略它;)