螺纹安全支架

时间:2015-09-26 09:26:50

标签: c++ multithreading c++11

编辑:我已将此问题移至codereview https://codereview.stackexchange.com/questions/105742/thread-safe-holder

我已经实现了一个线程安全持有者来安全地在线程之间传递数据。

用户可以多次设置值,但只有第一个SetIfEmpty调用存储该值,然后用户可以多次读取该值。

template <typename T>
class ThreadSafeHolder {
public:
    ThreadSafeHolder() : is_value_set_(false) {
    }

    void SetIfEmpty(const T& value) {
        std::lock_guard<std::mutex> lock(mutex_);
        // memory_order_relaxed is enough because storing to
        // `is_value_set_` happens only in `SetIfEmpty` methods
        // which are protected by mutex.
        if (!is_value_set_.load(std::memory_order_relaxed)) {
            new(GetPtr()) T(value);
            is_value_set_.store(true, std::memory_order_release);
        }
    }

    void SetIfEmpty(T&& value) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!is_value_set_.load(std::memory_order_relaxed)) {
            new(GetPtr()) T(std::move(value));
            is_value_set_.store(true, std::memory_order_release);
        }
    }

    //! This method might be safely call only if previous `IsEmpty()`
    //! call returned `false`.
    const T& Get() const {
        assert(!IsEmpty());
        return *GetPtr();
    }

    bool IsEmpty() const {
        // memory_order_acquire loading to become synchronize with
        // memory_order_release storing in `SetIfEmpty` methods.
        return !is_value_set_.load(std::memory_order_acquire);
    }

    ~ThreadSafeHolder() {
        if (!IsEmpty()) {
            GetPtr()->~T();
        }
    }

private:
    T* GetPtr() {
        return reinterpret_cast<T*>(value_place_holder_);
    }

    const T* GetPtr() const {
        return reinterpret_cast<const T*>(value_place_holder_);
    }

    // Reserved place for user data.
    char value_place_holder_[sizeof(T)];
    // Mutex for protecting writing access to placeholder.
    std::mutex mutex_;
    // Boolean indicator whether value was set or not.
    std::atomic<bool> is_value_set_;
};

问题

  • 一般来说代码是否正确?
  • 是否可以正确同步is_value_set_成员的访问权限?
  • 可以更轻松地访问is_value_set_会员吗?

应用

我想开发这样的holder来将活动异常从工作线程传递给主线程。

主线程:

ThreadSafeHolder<std::exception_ptr> exceptionPtrHolder;
// Run many workers.
// Join workers.
if (!exceptionPtrHolder.IsEmpty()) {
    std::rethrow_exception(exceptionPtrHolder.Get());
}

工作人员主题:

try {
    while (exceptionPtrHolder.IsEmpty()) {
        // Do hard work...
    }
} catch (...) {
    exceptionPtrHolder.SetIfEmpty(std::current_exception());
}

关于std::promise

的说明

std::promise在这里不合适(尽管std::promise::set_value是线程安全的)因为

  

如果没有共享状态或共享状态已存储值或异常,则抛出异常。

1 个答案:

答案 0 :(得分:0)

不,此代码不正确:T::~T()可能会被多次调用。您可能应该使用shared_ptr

你对活动异常的意思是什么?在抛出异常并且如何之后,工作线程是否继续执行?

我的意思是

  • 如果处理了异常,则没有理由将其转发到另一个线程,它已被处理。
  • 其他工作线程应该通过异常转发展开,并且可能由主线程重新启动,而std::promise对于此目的来说似乎并不太糟糕。

那么,如何在工作线程中重新设置另一个异常以及为什么呢?