C++ 螺纹保险夹
编辑:我将此问题移至codereview 我已经实现了一个线程安全保持器来在线程之间安全地传递数据 用户可以多次设置值,但只有第一个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
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());
}