Boost,指向互斥体的指针,这行得通吗? boost::mutex 和 std::vector,不可复制问题

发布于 2025-01-06 14:08:14 字数 464 浏览 2 评论 0原文

下面的代码会给我一个错误,因为 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());

它没有错误,但问题是“该互斥体实际上会按应有的方式工作吗?”这是将互斥体封装在类中,然后创建该类的向量的好方法吗?

谢谢。

The following code will give me an error, since boost::mutex is noncopyable, while xyz.push_back() is a copy constructor.

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

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

So I tried something like this,

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

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

It complies with no error, but the question is "will that mutex actually work as it should be?" Is this a good way to encapsulate a mutex inside a class, and then create a vector of that class?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

山色无中 2025-01-13 14:08:14

这里有两个问题:

  1. 互斥体是否会正确创建?

  2. 互斥体会被正确使用吗?

就问题而言,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 在这两种情况下都是共享资源,并且如果在创建后要更改它,也应该受到互斥锁的保护。然而,这样的锁会(正确地)破坏所有并行性..所以让我们暂时忽略它;)

There are two issues here:

  1. Will the mutex be created correctly?

  2. Will the mutex be used correctly?

As the question stands the answer to 1. is NO. The mutex pointer is not pointing to a mutex.

So you'll need to do to add an appropriate constructor.
And since you need a constructor you'll probably need to implement a destructor, copy constructor and assignment operator if you want your class to behave correctly.

Either

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; }

or

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; }

Would be what I'd expect.

In the first case each instance and copy of the object has its own mutex. In the second each object shares a mutex with its copies, but the mutex must be created before the instances.

There is a third variant where the mutex can be created by the constructor and shared with all the instances, but to do this you hold a shared_ptr to the mutex instead of a raw pointer.

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.
};

If we go down any of these paths we end up in a situation where the mutex is correctly created and initialised.

Now for the tricky part "Will the mutex be used correctly?". To answer this we need to know how the objects are shared between threads, what is the shared data the mutex is supposed to be protecting.

If the vector of objects is created in the main thread before any worker threads are created, and each object instance is getting modified by worker threads ( so that the mutex really is protecting the x data) then the first version if probably correct. In this case your code looks more like this.

//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();

If x is really a pointer type and the thing it is pointing to is shared between threads then your code probably looks like this, and the correct version of the code to use is 2 or 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();

The key being that there is one mutex per shared resource.

Note that technically the vector v is a shared resource in both cases, and should also be protected by a mutex if it is going to be changed after creation. However such a lock would (correctly) destroy all parallelism .. so lets ignore that for now ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文