1

我真的很感激关于这个问题的一些建议。

例如

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

感谢您提前提出任何意见和意见。

法提赫

4

4 回答 4

2

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

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

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

于 2010-12-15T12:51:02.350 回答
2

你听说过得墨忒耳法则吗?

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

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

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

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

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

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

于 2010-12-15T13:29:17.137 回答
2

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

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

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

于 2010-12-15T13:32:06.733 回答
1

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

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

当然,很容易看出上面的代码为什么不好,但实际代码要复杂得多,而且在锁被释放后,指针可能会在很多地方徘徊,这很难确定。

于 2010-12-15T12:46:00.180 回答