C++ getter / setter、互斥锁、细粒度锁定

发布于 2025-01-07 23:23:37 字数 854 浏览 0 评论 0原文

我有一个由多个线程共享的对象,我想锁定单个成员变量,而不锁定整个对象,以便不同的线程可以同时访问不同的成员变量。 阅读一些文章后,我使用shared_mutex和getter() / setter()函数编写代码。

    class Test
    {
    public:
    **// variable, shared_mutex and getter/setter for x**
    double x;
    boost::shared_mutex x_mutex;
    double x_getter();
    void x_setter();
    **// variable, shared_mutex and getter/setter for y**
    ......
    **// variable, shared_mutex and getter/setter for z**
    ......
    };

    double Test::x_getter()
    {
      // get shared access
      boost::shared_lock lock(_access);
      return x;
    }

    void Test::x_setter()
    {
      // get exclusive access
      boost::unique_lock lock(_access);
      // do something with x;
    }

    //getter/setter functions for y and z. 
    ......

代码看起来很笨拙,尤其是当成员变量的数量增加时。我想知道是否有更好的解决方案来解决此类问题。

谢谢。

I have an object which is shared by multiple threads, I want to lock individual member variables, without locking the entire object, so that different threads can access to different member variables at the same time.
After reading some articles, I write the code using shared_mutex and getter() / setter() functions.

    class Test
    {
    public:
    **// variable, shared_mutex and getter/setter for x**
    double x;
    boost::shared_mutex x_mutex;
    double x_getter();
    void x_setter();
    **// variable, shared_mutex and getter/setter for y**
    ......
    **// variable, shared_mutex and getter/setter for z**
    ......
    };

    double Test::x_getter()
    {
      // get shared access
      boost::shared_lock lock(_access);
      return x;
    }

    void Test::x_setter()
    {
      // get exclusive access
      boost::unique_lock lock(_access);
      // do something with x;
    }

    //getter/setter functions for y and z. 
    ......

The code looks clumsy, especially when the number of member variables increases. I am wondering whether there is better solutions out there for this type of problem.

Thanks.

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

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

发布评论

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

评论(3

冰火雁神 2025-01-14 23:23:37

由于您显然只在实际读取/写入数据的短时间内需要锁,因此您可以将其与受控数据一起封装到一个类型中,然后将其用作成员变量:

// note: you probably should add constructors as well
template<typename T> struct synchronized
{
public:
  synchronized& operator=(T const& newval)
  {
    boost::unique_lock lock(mutex);
    value = newval;
  }
  operator T() const
  {
    boost::unique_lock lock(mutex);
    return value;
  }
private:
  T value;
  boost::shared_mutex mutex;
};

class Test
{
public:
  synchronized<double> x;
  synchronized<int> y;
  synchronized<std::string> z;
};

void foo(Test& t)
{
  double read = t.x; // locked, via synchronized<double>::operator double() const
  t.x = 3.14;        // locked, via synchronized<double>::operator=
}

Since you obviously need the lock only for the short time of actually reading/writing the data, you could just encapsulate it with the controlled data into a type which you then use as member variables:

// note: you probably should add constructors as well
template<typename T> struct synchronized
{
public:
  synchronized& operator=(T const& newval)
  {
    boost::unique_lock lock(mutex);
    value = newval;
  }
  operator T() const
  {
    boost::unique_lock lock(mutex);
    return value;
  }
private:
  T value;
  boost::shared_mutex mutex;
};

class Test
{
public:
  synchronized<double> x;
  synchronized<int> y;
  synchronized<std::string> z;
};

void foo(Test& t)
{
  double read = t.x; // locked, via synchronized<double>::operator double() const
  t.x = 3.14;        // locked, via synchronized<double>::operator=
}
源来凯始玺欢你 2025-01-14 23:23:37

你是对的,这种方法看起来确实很笨拙,而且很快就会变得难以管理。因此,我尝试通过打破数据依赖性来建模多线程问题。但是,如果没有进一步了解您要解决的问题,我无法建议如何对问题进行建模。

如果您已经投资了这样的架构并且改变已经太晚了,那么我会考虑这一点。

template<class T>
class SharedValiable
{
    private:
        T                    myT;
        boost::shared_mutex  myTMutex;

    public:
        //
        // Implement appropriate copy, assign and default 
        // to ensure proper value semantics
        //

        T getter() const
        {
            boost::shared_lock lock(_access);
            return x;
        }

        void setter()
        {
            boost::unique_lock lock(_access);
        }
}

这允许每个变量按照您最初的预期受到保护,但更容易添加新成员或从类中删除成员。此外,模板可以专门用于可以使用原子操作系统操作(例如整数)的某些类型。例如:

template<int>
class SharedValiable
{
    private:
        T                    myT;

    public:
        //
        // Implement appropriate copy, assign and default 
        // to ensure proper value semantics
        //

        T getter() const
        {
            // no need to lock, updates are atomic
            return x;
        }

        void setter()
        {
            // no mutex needed we will use an atomic OS op to update
            InterlockedCompareAndExchange(myT, newVal);
        }
}

You are correct that this method does look quite clumsy and quickly becomes unmanageable. As such I try to model multithreaded problems by breaking data dependence. However, without further context to what you are trying to solve, I can not advise how to model the problem.

If you are already invested in such architecture and its too late to change then I'd consider this.

template<class T>
class SharedValiable
{
    private:
        T                    myT;
        boost::shared_mutex  myTMutex;

    public:
        //
        // Implement appropriate copy, assign and default 
        // to ensure proper value semantics
        //

        T getter() const
        {
            boost::shared_lock lock(_access);
            return x;
        }

        void setter()
        {
            boost::unique_lock lock(_access);
        }
}

This allows each variable to be protected as you originally intended, but makes it easier to add new or remove members from classes. Furthermore, the template can be specialized for certain type that can use atomic OS operations such as ints. e.g.:

template<int>
class SharedValiable
{
    private:
        T                    myT;

    public:
        //
        // Implement appropriate copy, assign and default 
        // to ensure proper value semantics
        //

        T getter() const
        {
            // no need to lock, updates are atomic
            return x;
        }

        void setter()
        {
            // no mutex needed we will use an atomic OS op to update
            InterlockedCompareAndExchange(myT, newVal);
        }
}
眉目亦如画i 2025-01-14 23:23:37

一般来说,我建议不要这样做。通常,您尝试对类执行的操作是确保在进入和离开方法时对对象的状态提供某种保证。例如,在列表对象中,您需要链接处于一致状态,需要计数正确等等。仅锁定单个变量很少会导致这种情况发生。如果您更改链接而不锁定计数状态,那么当更新过程中另一个线程查询该链接时,您就对其中一个或另一个撒谎了。更糟糕的是,您可能会同时更改两个链接,最终会得到一个不再有效的混乱列表,具体取决于您的存储空间。当然,这只是可能的例子之一,但您应该明白这一点。

如果您希望多个线程能够查看对象的状态,那么您需要对整个对象进行读取器/写入器锁定,这将允许任意数量的读取器,但不允许他们在更新中查看对象。

In general, I would advise against this. What you are trying to do, generally, with a class is to ensure some sort of guarantee on the state of an object when you enter and leave a method. For instance, in a list object, you need the links to be in a consistent state, you need the count to be correct, etc. Just locking individual variables rarely allows that to happen. If you change a link without also locking the state of the count, you are lying about one or the other when it's queried by a different thread in the middle of an update. Worse, you could change two links at the same time and end up with a screwy list that won't work anymore, depending on your storage. Of course that's one example among may possible, but you should get the idea.

If you want multiple threads to be able to view the state of your object, then you want a reader/writer lock on the whole object, which would allow as many readers as you like, but not allow them to see the object mid-update.

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