简体   繁体   English

我应该使用const使对象成为线程安全的吗?

[英]Should I use const to make objects thread-safe?

I wrote a class which instances may be accessed by several threads. 我写了一个类,可以通过多个线程访问实例。 I used a trick to remember users they have to lock the object before using it. 我使用了一个技巧来记住用户,他们必须在使用对象之前将其锁定。 It involves keeping only const instances. 它涉及仅保留const实例。 When in the need to read or modify sensitive data, other classes should call a method (which is const, thus allowed) to get a non-const version of the locked object. 当需要读取或修改敏感数据时,其他类应调用一个方法(因此是const,因此是允许的)以获取锁定对象的非const版本。 Actually it returns a proxy object containing a pointer to the non-const object and a scoped_lock, so it unlocks the object when going out of scope. 实际上,它返回一个包含指向非const对象的指针和scoped_lock的代理对象,因此在超出范围时会解锁该对象。 The proxy object also overloads operator-> so the access to the object is transparent. 代理对象也使operator->重载,因此对该对象的访问是透明的。

This way, shooting onself's foot by accessing unlocked objects is harder (there is always const_cast). 这样,通过访问未锁定的对象来射击自己的脚就更难了(总是有const_cast)。

"Clever tricks" should be avoided, and this smells bad anyway. 应该避免“聪明的把戏”,反正这闻起来很不好。

Is this design really bad ? 这个设计真的不好吗? What else can I or should I do ? 我还能或应该做什么?

Edit: Getters are non-const to enforce locking. 编辑:吸气剂不是强制执行锁定的常量。

Basic problem: a non-const reference may exist elsewhere. 基本问题:非常量引用可能存在于其他地方。 If that gets written safely, it does not follow that it can be read safely -- you may look at an intermediate state. 如果将其安全地写入 ,就不能保证可以安全地读取它-您可能会看到中间状态。

Also, some const methods might (legitimately) modify hidden internal details in a thread-unsafe way. 同样,某些const方法可能(合法)以线程不安全的方式修改隐藏的内部细节。

Analyse what you're actually doing to the object and find an appropriate synchronisation mode . 分析您对对象实际执行的操作,然后找到合适的同步模式

If your clever container really does know enough about the objects to control all their synchronisation via proxies, then make those objects private inner classes. 如果您的聪明容器确实对对象足够了解,可以通过代理控制所有对象的同步,则将这些对象设为私有内部类。

This is clever, but unfortunately doomed to fail. 这很聪明,但不幸的是注定要失败。

The problem, underlined by spraff , is that you protect against reads but not against writes. spraff强调的问题是,您可以防止读取,但不能写入。

Consider the following sequence: 请考虑以下顺序:

unsigned getAverageSalary(Employee const& e) {
  return e.paid() / e.hired_duration();
}

What happens if we increment paid between the two function calls ? 如果我们增加会发生什么paid两个函数调用之间? We get an incoherent value. 我们得到一个不一致的价值。

The problem is that your scheme does not explicitly enforce locking for reads. 问题在于您的方案没有显式强制读取锁定。

Consider the alternative of a Proxy pattern: The object itself is a bundle of data, all privates. 考虑代理模式的另一种选择:对象本身是一束数据,都是私有的。 Only a Proxy class (friend) can read/write its data, and when initializing the Proxy it grabs the lock (on the mutex of the object) automatically. 只有Proxy类(朋友)才能读取/写入其数据,并且在初始化Proxy它将自动获取(在对象的互斥体上)锁。

class Data {
  friend class Proxy;
  Mutex _mutex;
  int _bar;
};

class Proxy {
public:
  Proxy(Data& data): _lock(data._mutex), _data(data) {}

  int bar() const { return _data._bar; }
  void bar(int b) { _data._bar = b; }

private:
  Proxy(Proxy const&) = delete; // disable copy

  Lock _lock;
  Data& _data;
};

If I wanted to do what you are doing, I would do one of the following. 如果我想做您正在做的事情,请执行以下一项操作。

Method 1: 方法1:

shared_mutex m;  // somewhere outside the class

class A
{
private:
    int variable;
public:
    void lock() { m.lock(); }
    void unlock() { m.unlock(); }
    bool is_locked() { return m.is_locked(); }
    bool write_to_var(int newvalue)
    {
        if (!is_locked())
            return false;
        variable = newvalue;
        return true;
    }
    bool read_from_var(int *value)
    {
        if (!is_locked() || value == NULL)
            return false;
        *value = variable;
        return true;
    }
};

Method 2: 方法2:

shared_mutex m;  // somewhere outside the class

class A
{
private:
    int variable;
public:
    void write_to_var(int newvalue)
    {
        m.lock();
        variable = newvalue;
        m.unlock();
    }
    int read_from_var()
    {
        m.lock();
        int to_return = variable;
        m.unlock();
        return to_return;
    }
};

The first method is more efficient (not locking-unlocking all the time), however, the program may need to keep checking the output of every read and write to see if they were successful. 第一种方法效率更高(不是一直都锁定-解锁),但是,程序可能需要不断检查每个读写的输出,以查看它们是否成功。 The second method automatically handles the locking and so the programmer wouldn't even know the lock is there. 第二种方法自动处理锁定,因此程序员甚至都不知道锁定在那儿。

Note: This is not code for copy-paste. 注意:这不是复制粘贴的代码。 It shows a concept and sketches how it's done. 它显示了一个概念并概述了它是如何完成的。 Please don't comment saying you forgot some error checking somewhere. 请不要发表评论说您忘记了某些地方的错误检查。

This sounds a lot like Alexandrescu's idea with volatile . 这听起来很像Alexandrescu关于volatile的想法。 You're not using the actual semantics of const , but rather exploiting the way the type system uses it. 您不是在使用const的实际语义,而是在使用类型系统使用const的方式。 In this regard, I would prefer Alexandrescu's use of volatile : const has very definite and well understood semantics, and subverting them will definitely cause confusion for anyone reading or maintaining the code. 在这方面,我希望Alexandrescu使用volatileconst具有非常明确和易于理解的语义,颠覆它们肯定会给阅读或维护代码的人造成混乱。 volatile is more appropriate, as it has no well defined semantics, and in the context of most applications, is not used for anything else. volatile更合适,因为它没有明确定义的语义,并且在大多数应用程序的上下文中,不用于任何其他用途。

And rather than returning a classical proxy object, you should return a smart pointer. 而不是返回经典的代理对象,您应该返回一个智能指针。 You could actually use shared_ptr for this, grabbing the lock before returning the value, and releasing it in the deleter (rather than deleting the object); 您实际上可以使用shared_ptr ,在返回值之前先获取锁,然后将其释放到删除器中(而不是删除对象); I rather fear, however, that this would cause some confusion amongst the readers, and I would probably go with a custom smart pointer (probably using shared_ptr with the custom deleter in the implementation). 但是,我相当担心,这会在读者之间引起一些混乱,并且我可能会使用自定义智能指针(可能在实现中将shared_ptr与自定义删除器一起使用)。 (From your description, I suspect that this is closer to what you had in mind anyway.) (根据您的描述,我怀疑这与您的想法更接近。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM