如何防止多个线程在cpp中同时使用单例class实例
How to prevent multiple threads to use singleton class instance at same time in cpp
我正在编写一个线程安全的单例class,如下所示。以下实现确保仅创建 class 的一个实例。我的用例是我在多线程环境中使用这个实例,其中每个线程都可以调用 getInstance()
并使用该实例做一些工作。我的问题是如何确保在特定时间只有一个线程正在使用该实例,以防止在多个线程同时尝试使用单个实例时可能出现的竞争条件。
class Singleton {
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton s;
return s;
}
};
您可以做的一件事是通过锁定互斥体使其所有成员线程安全。
class Singleton {
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
std::mutex my_mutex_member;
public:
static Singleton& getInstance() {
static Singleton s;
return s;
}
void my_singleton_cool_function()
{
std::lock_guard<std::mutex> lg(my_mutex_member);
// cool code here
}
};
在上面的示例中,lg
将锁定互斥量,并且在函数结束时,当 lg
被销毁时,析构函数将解锁互斥量。这意味着只有一个线程可以同时 运行 该函数。这允许所有线程都有对单例的引用,并且只有在两个或多个线程试图同时做同样的事情时才会阻塞。
My question is how can I ensure that only one thread is using the instance at a particular time.
作为一般规则,你不能。
Nathan Oliver 的回答适用于其他模块 "use the instance" 的唯一方法是调用它的方法的特殊情况。在这种情况下,您可以确保这些方法中的每一个都锁定相同的互斥量。
但是,如果你的单例公开了任何 public
数据成员,那么所有的赌注都没有了:你将无法控制其他模块如何 "use" 你的对象的 public 数据成员。
小心!即使您将所有数据成员设为私有,并且将单例对象设为真正 "thread safe;" still 也不能保证可能依赖于某些的其他代码的线程安全您的单例对象与其他一些数据之间的关系。
线程安全并不是真正关于确保"only one thread uses the object at a time."线程安全是关于保留invariant relationships. For example, if you have a doubly-linked ring数据结构,那么一个重要的不变量是,p->next->prev
必须始终等于 p
。
想要将新元素拼接到环中的线程必须暂时打破不变量。 "Thread safety" 在这种情况下意味着确保没有其他线程能够看到暂时中断的状态。
用 "thread safe" 个对象构建程序并不能构成整个程序 "thread safe",因为更高级别的程序可能依赖于对象之间重要的不变关系。而且,即使对象 单独 线程安全,它们也无法意识到对整个程序有意义的更高级别的关系。
我正在编写一个线程安全的单例class,如下所示。以下实现确保仅创建 class 的一个实例。我的用例是我在多线程环境中使用这个实例,其中每个线程都可以调用 getInstance()
并使用该实例做一些工作。我的问题是如何确保在特定时间只有一个线程正在使用该实例,以防止在多个线程同时尝试使用单个实例时可能出现的竞争条件。
class Singleton {
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
static Singleton s;
return s;
}
};
您可以做的一件事是通过锁定互斥体使其所有成员线程安全。
class Singleton {
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
std::mutex my_mutex_member;
public:
static Singleton& getInstance() {
static Singleton s;
return s;
}
void my_singleton_cool_function()
{
std::lock_guard<std::mutex> lg(my_mutex_member);
// cool code here
}
};
在上面的示例中,lg
将锁定互斥量,并且在函数结束时,当 lg
被销毁时,析构函数将解锁互斥量。这意味着只有一个线程可以同时 运行 该函数。这允许所有线程都有对单例的引用,并且只有在两个或多个线程试图同时做同样的事情时才会阻塞。
My question is how can I ensure that only one thread is using the instance at a particular time.
作为一般规则,你不能。
Nathan Oliver 的回答适用于其他模块 "use the instance" 的唯一方法是调用它的方法的特殊情况。在这种情况下,您可以确保这些方法中的每一个都锁定相同的互斥量。
但是,如果你的单例公开了任何 public
数据成员,那么所有的赌注都没有了:你将无法控制其他模块如何 "use" 你的对象的 public 数据成员。
小心!即使您将所有数据成员设为私有,并且将单例对象设为真正 "thread safe;" still 也不能保证可能依赖于某些的其他代码的线程安全您的单例对象与其他一些数据之间的关系。
线程安全并不是真正关于确保"only one thread uses the object at a time."线程安全是关于保留invariant relationships. For example, if you have a doubly-linked ring数据结构,那么一个重要的不变量是,p->next->prev
必须始终等于 p
。
想要将新元素拼接到环中的线程必须暂时打破不变量。 "Thread safety" 在这种情况下意味着确保没有其他线程能够看到暂时中断的状态。
用 "thread safe" 个对象构建程序并不能构成整个程序 "thread safe",因为更高级别的程序可能依赖于对象之间重要的不变关系。而且,即使对象 单独 线程安全,它们也无法意识到对整个程序有意义的更高级别的关系。