如何安全地删除std::互斥体成员
How to safely delete member std::mutex?
我最近一直在使用std::mutex,现在正在寻找关于删除以std::mutex为成员的对象的模式/设计指南。当一个对象作为使用互斥锁的公共函数(监视函数、临界区等)并且当这个对象被删除时,它可能有线程仍在等待互斥锁时,问题就出现了。当Std::mutex被其他线程锁定时,会删除未定义的行为,因此会出现问题。
我想知道在这种情况下使用的一般可接受的模式是什么。或者,如果这被认为是一种糟糕的编码风格,一种避免这种设计的方法,即不要删除互斥锁方法仍在等待的对象。
的例子:
//a public method that uses mutex.
IAsyncAction^ XInputBase::flushTask()
{
return create_async([this](){
_monitorMutex.lock();
if (_readyToFlush && !_noMoreFlush) {
//should flush only once, block additional flush signals.
_noMoreFlush = true;
_monitorMutex.unlock();
//actually flush
concurrency::task<void> UITask = concurrency::create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler([=]()
{
onFlush();
})));
}
else {
_needToFlush = true;
_monitorMutex.unlock();
}
});
}
尝试解决方案:在所有等待线程被处理后,等待互斥锁完全解锁的析构函数。我通过在互斥锁外设置一个标志来实现它,这样方法中的所有线程要么退出,要么等待互斥锁。然后在析构函数中最后一次锁定std::互斥锁。如果std::互斥对象是公平的,那么析构函数应该在其他等待线程处理完之后才被解锁,然后销毁对象。
这不起作用,因为std::mutex在大多数平台上不能保证公平性。
工作解决方案:将对象实现为ref类或其他引用计数对象(智能指针)。然后将并发方法实现为保存对对象的强引用的lambda。一旦持有该对象的所有其他对象被删除,并且所有具有互斥锁的lambda都被处理,该对象将被自动删除。
我不喜欢这个方法,因为它对其余的代码造成了一些限制。对于WinRT,这个方法没有给出哪个线程删除这个对象的控制,因此,可能会发生UI线程错误。
如果对象中有数据,则无法保护对象的生命周期。
可以使用外部互斥锁来保护对象。
那么从这个开始:
template<class T>
struct locked_view {
template<class F>
auto operator->*(F&& f) const
-> std::result_of_t< F(T const&) >
{
auto l = lock();
return std::forward<F>(f)(*t);
}
locked_view(locked_view const&) = default;
locked_view& operator=(locked_view const&) = default;
locked_view()=default;
explicit operator bool() const { return m&&t; }
locked_view( std::mutex* min, T* tin ):m(min), t(tin) {}
private:
std::unique_lock<std::mutex> lock() const {
return std::unique_lock<std::mutex>(*m);
}
std::mutex* m;
T* t;
};
template<class T>
struct locked {
locked()=default;
locked(locked&& o):
t( o.move_from() )
{}
locked(locked const& o):
t( o.copy_from() )
{}
locked& operator=(locked&& o) {
auto tin = o.move_from();
assign_to(std::move(tin));
return *this;
}
locked& operator=(locked const& o) {
auto tin = o.copy_from();
assign_to(std::move(tin));
return *this;
}
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, locked>{}, int> =0
>
locked( U&& u ):
t( std::forward<U>(u) )
{}
// stars of show:
locked_view<T const> read() const
{
return {&m, std::addressof(t)};
}
locked_view<T> write()
{
return {&m, std::addressof(t)};
}
T move_from() {
return write()->*[](T& tin){return std::move(tin);};
}
T copy_from() const {
return read()->*[](T const& tin){return tin;};
}
template<class U>
void assign_to( U&& u ) {
write()->*[&](T& t){ t = std::forward<U>(u); };
}
private:
mutable std::mutex m;
T t;
};
用法如下:
locked<int> my_int = 7;
my_int.read()->*[](int x){ std::cout << x << 'n'; };
下一步,把一个std::unique_ptr<YourClass>
塞进去。这将允许人们删除它。
最后,共享一个std::shared_ptr<>
给它。
template<class T>
using shared_locked = std::shared_ptr< locked< T > >;
template<class T>
using shared_locked_ptr = shared_locked< std::unique_ptr<T> >;
是你的类型。
要使用,他们这样做:
shared_locked_ptr<widget> w;
w->read()->*[&]( auto& ptr ) {
if (ptr) ptr->do_something();
};
检查被锁定块内的生命周期
线程安全删除对象:
w->write()->*[&]( auto& ptr ) {
ptr = {};
};
锁在指向小部件的指针周围,而不是小部件周围。指向小部件指针的指针是共享的。
代码未测试,但类似的设计以前工作过。
- std::ofstream 作为类成员删除复制构造函数?
- 使用 std::index_sequence 初始化具有固定大小数组成员的 POD 结构容器
- std::to_string - 'to_string' 不是 'std' 的成员 - Visual Studio Code 1.42.0
- 使用 std::async 时死锁,将来作为成员
- 可变参数模板参数扩展 类型为 std::function 的类成员
- FreeFileSync C++错误:'byte'不是 'std' 的成员
- 为什么 std::sort 找不到合适的(静态成员)函数重载?
- 对带有唯一指针的 std::thread 使用类成员函数时出现编译错误
- 在模板化成员函数的返回类型中使用 std::enable_if 时的编译器差异
- 有没有办法避免为 std::variant 类成员中的所有类型编写构造函数?
- 是否允许类类型的 std::function 成员变量(不完整类型)?
- 访问 std:vector 的类成员 std:vector 在一个类中与另一个 std:vector
- std::异步与非静态成员函数
- 如何在 c++11 中静态断言 std::array 类成员进行排序?
- 将初始化器列表/聚合初始化转发到 std::array 成员
- C++ IDE 不会推断/自动完成对模板类中的 std::array 下标表达式的成员访问
- 在哪里可以找到 std::bitset 的数据成员?
- 没有'str'成员在 GCC 和 Clang 'std::basic_ostream<char>',但 MSVC 没有问题
- unique_ptr问题:不是'std'成员
- std::成员函数的异步调用