C++ 螺纹保险夹

C++ 螺纹保险夹,c++,multithreading,c++11,C++,Multithreading,C++11,编辑:我将此问题移至codereview 我已经实现了一个线程安全保持器来在线程之间安全地传递数据 用户可以多次设置值,但只有第一个SetIfEmpty调用存储该值,然后用户可以多次读取该值 template <typename T> class ThreadSafeHolder { public: ThreadSafeHolder() : is_value_set_(false) { } void SetIfEmpty(const T& value

编辑:我将此问题移至codereview

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

用户可以多次设置值,但只有第一个
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_;
};
工作线程:

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

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

如果没有共享状态或共享状态已存储值或异常,则引发异常


不,此代码不正确:
T::~T()
可能被多次调用。您可能应该使用
共享\u ptr

“活动异常”是什么意思?工作线程在抛出异常后是否继续执行?如何执行

我是说

  • 如果处理了异常,那么就没有理由将其转发到另一个线程,因为它已经被处理了
  • else工作线程应该是无意识的,带有异常转发,并且可能由主线程重新启动,
    std::promise
    对于这个目的来说似乎不太糟糕

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

您可能希望使用字符数组来代替您的字符数组。因此,这个问题可能更适合您。哦,我无意中在这里发布了它,谢谢您的指点。这个关于codereview的问题,我没听懂
T::~T()
仅在
ThreadSafeStorage::~ThreadSafeHolder()
中调用,该函数调用一次,因为只使用了
ThreadSafeStorage
的一个对象。我想在许多工作线程中运行一些函数。函数可能会抛出,如果它抛出一个worker,我想停止其他worker线程并处理主线程中的异常。并且
ThreadSafeStorage::~ThreadSafeHolder()
可能会被多次调用。普通,它命名为副本。如果是这样,为什么将结果或异常转发到主线程的
std::promise
std::fututre
不好?在工作线程之间,您可以使用
std::shared\u ptr
并进行检查。对不起,不是
std::shared\u ptr
而是
std::shared\u ptr
。所以,既然设置了异常(意味着执行被中断,异常被转发到主线程),就没有办法设置另一个异常,也没有问题,只需退出线程并加入它。
std::shared\u ptr
将无法工作,因为无法测试
std::promise
并自动设置异常,然后我们陷入了两次设置异常的危险中(从两个线程),但是“如果没有共享状态或者共享状态已经存储了值或异常,则会引发异常。”不,您是不对的:您可以在try-catch块中设置异常。但无论如何,并没有理由测试是否设置了异常。就这么定了!然后抓住并退出线程。
try {
    while (exceptionPtrHolder.IsEmpty()) {
        // Do hard work...
    }
} catch (...) {
    exceptionPtrHolder.SetIfEmpty(std::current_exception());
}