【问题标题】:sharing a data member with the outside world in a thread-safe manner以线程安全的方式与外界共享数据成员
【发布时间】:2011-05-25 21:29:34
【问题描述】:

我非常感谢您就此事提出建议。

例如

class Foo
{
    TData data;
public:
    TData *getData() { return &data; } // How can we do this in a thread safe manner ?
};

所以我想要一种机制来使getData() 线程安全。我提出了我自己的解决方案,其中涉及将以下模板类中的数据成员与用于同步访问它的互斥锁一起打包。你怎么看 ?可能会出现什么问题?

class locked_object : boost::noncopyable
{
    T *object;
    TLockable *lock;
    bool locked;

public:
    locked_object(T *_object, TLockable *_lock) : object(_object), lock(_lock), locked(true)
    {
        lock->lock();
    }

    ~locked_object()
    {
        lock->unlock();
    }

    T *get()
    {
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");
        return this->tobject;
    }


    void unlock()
    {
        locked = false;

        lock->unlock();
    }

    T *operator ->() const
    {   
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");

        return this->tobject; 
    }

    operator T *() const
    {
        _ASSERT(locked);
        if (!locked)
            throw new std::exception("Synchronization error ! Object lock is already released !");

        return this->tobject;
    }
};

提前感谢您的任何cmets和意见。

法提赫

【问题讨论】:

  • 您不需要在缩进的代码块中编写&gt(无论如何都缺少;)。只需写>。
  • 如果你能得到一份副本,看看 Alexandrescu 的 Modern C++ Design。

标签: c++ multithreading synchronization locking resource-management


【解决方案1】:

你听说过The Law of Demeter吗?

有一个类似的建议(我认为来自 Sutter):不要分享对您内部的参考

两者都是为了避免耦合,因为通过共享对您的内部的引用,这意味着您的公共接口会泄露实现细节。

既然这么说了,你的界面就不行了。

问题是您锁定的是代理,而不是对象:我仍然可以通过多个路径访问:

  • 来自Foo,不需要互斥锁 --> oups ?
  • 来自两个不同的locked_object --> 这似乎不是故意的......

更重要的是,一般来说,您不能锁定对象的单个部分,因为这样您就不能对整个对象拥有事务语义。

【讨论】:

    【解决方案2】:

    您的设计让用户有责任确保在正确的时间锁定和解锁对象。即使您锁定的对象进行错误检查,它也不会涵盖所有基础(例如在完成时忘记释放对象)

    假设你有 Unsafe 对象TData。您将其包装在Foo 中,但不是Foo 返回指向TData 的指针,而是在Foo 中重新实现TData 中的所有公共方法,但使用锁定和解锁。

    这与pImpl 模式非常相似,只是您的接口在调用实现之前添加了锁。这样用户只知道对象是线程安全的,不需要担心同步。

    【讨论】:

      【解决方案3】:

      这是多线程的核心问题,你不能要求客户端代码以线程安全的方式使用你的对象。你也不能真正做任何事情来帮助客户端代码落入成功的陷阱,它必须自己处理锁定。将编写代码的责任交给最不可能正确编写代码的人。

      您可以通过从访问器返回对象的 副本 来简化操作。这是线程安全的,一个线程只拥有一个副本。您可能应该使该副本的类型不可变,以再次强调修改对象不太可能产生预期的结果。一个无法解决的副作用可能会严重影响这个副本的定义是陈旧的。这些是可能弊大于利的创可贴。

      记录方法的填充,以便客户端程序员知道该怎么做。

      【讨论】:

        【解决方案4】:

        这不是特别安全。没有什么能阻止用户把事情搞砸:

        locked_object<T> o(...);
        T* t = o.get();
        o.unlock();
        t->foo(); // Bad!
        

        当然,很容易看出上面的代码为什么不好,但真正的代码要复杂得多,而且在锁被释放后指针悬空的方式有很多,很难确定。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-10
          • 2018-12-30
          相关资源
          最近更新 更多