如何以线程安全的方式实现PIMPL成语?
我有一个使用。该类的一个实例将在多线程程序中的多个线程中使用。在类的实现中,我确保在此类可以读/编写自己的数据或使用共享资源的任何地方都锁定了锁定。
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
////////////////////////////////////////////////////////////////////////////////
// Define the "Foo" class using the PIMPL idiom
class Foo final
{
public:
Foo();
~Foo();
void DoSomething(const int thread_num) const;
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
////////////////////////////////////////////////////////////////////////////////
// Define the implementation of "Foo"
struct Foo::Impl final
{
void DoSomething(const int thread_num) const {
// Lock before accessing the shared resource of "std::cout"
std::lock_guard<std::mutex> lock(m_mutex);
std::cout << "Doing something from thread " << thread_num << std::endl;
}
private:
mutable std::mutex m_mutex;
};
////////////////////////////////////////////////////////////////////////////////
// Forward calls to the implementation
Foo::Foo()
: m_impl(new Impl()) // Using C++11 so std::make_unique is not available
{}
Foo::~Foo() = default;
void Foo::DoSomething(const int thread_num) const
{
// POSSIBLE LOCKING POINT A
// Is dereferencing the "m_impl" pointer safe to do from multiple threads
// without additional locking?
m_impl->DoSomething(thread_num);
}
////////////////////////////////////////////////////////////////////////////////
// Use "Foo"
int main()
{
Foo foo;
// Is using "Foo" without locking safe here because it does a lock inside
// DoSomething before using the shared resource "cout"?
std::thread t1([&] {
// POSSIBLE LOCKING POINT B
foo.DoSomething(1);
});
std::thread t2([&] {
// POSSIBLE LOCKING POINT B
foo.DoSomething(2);
});
t1.join();
t2.join();
}
问题是...这足以成为线程安全吗?我还需要锁定m_impl
(可能的锁定点A),还是定义得当?为了进一步,我需要在多个线程之间(可能的锁定点B)之间访问foo
,还是定义得当?
我的代码似乎始终如一地运行,没有它锁定在上面的方式,但我只想验证我没有任何不确定的行为。
如果上述示例中的锁定不够,那么锁定的正确方法是什么?
I have a class that is implemented using the PIMPL idiom. A single instance of this class will be used across multiple threads within a multithreaded program. Inside the implementation of the class I made sure I did locking anywhere this class may read/write its own data, or use a shared resource.
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
////////////////////////////////////////////////////////////////////////////////
// Define the "Foo" class using the PIMPL idiom
class Foo final
{
public:
Foo();
~Foo();
void DoSomething(const int thread_num) const;
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
////////////////////////////////////////////////////////////////////////////////
// Define the implementation of "Foo"
struct Foo::Impl final
{
void DoSomething(const int thread_num) const {
// Lock before accessing the shared resource of "std::cout"
std::lock_guard<std::mutex> lock(m_mutex);
std::cout << "Doing something from thread " << thread_num << std::endl;
}
private:
mutable std::mutex m_mutex;
};
////////////////////////////////////////////////////////////////////////////////
// Forward calls to the implementation
Foo::Foo()
: m_impl(new Impl()) // Using C++11 so std::make_unique is not available
{}
Foo::~Foo() = default;
void Foo::DoSomething(const int thread_num) const
{
// POSSIBLE LOCKING POINT A
// Is dereferencing the "m_impl" pointer safe to do from multiple threads
// without additional locking?
m_impl->DoSomething(thread_num);
}
////////////////////////////////////////////////////////////////////////////////
// Use "Foo"
int main()
{
Foo foo;
// Is using "Foo" without locking safe here because it does a lock inside
// DoSomething before using the shared resource "cout"?
std::thread t1([&] {
// POSSIBLE LOCKING POINT B
foo.DoSomething(1);
});
std::thread t2([&] {
// POSSIBLE LOCKING POINT B
foo.DoSomething(2);
});
t1.join();
t2.join();
}
The question is... is this enough to be thread-safe? Do I also need to lock before dereferencing m_impl
(POSSIBLE LOCKING POINT A), or is that well defined? To take it even further do I need to lock before even accessing foo
at all between multiple threads (POSSIBLE LOCKING POINT B), or is that well defined?
My code seems to run consistently without issues the way it is locked above, but I just want to verify that I don't have any undefined behavior.
If the locking in the above example is not sufficient, then what is the correct way to lock in order to use a PIMPL class in a thread-safe manner?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论