多个文本说,当在.NET中实现双重检查锁定时,您锁定的字段应该应用volatile修饰符.但为什么呢?考虑以下示例:
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
Run Code Online (Sandbox Code Playgroud)
为什么不"锁定(syncRoot)"完成必要的内存一致性?在"lock"语句之后,读取和写入都是不稳定的,因此必须实现必要的一致性,这不是真的吗?
以下单例实现数据 - 竞争是免费的吗?
static std::atomic<Tp *> m_instance;
...
static Tp &
instance()
{
if (!m_instance.load(std::memory_order_relaxed))
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_instance.load(std::memory_order_acquire))
{
Tp * i = new Tp;
m_instance.store(i, std::memory_order_release);
}
}
return * m_instance.load(std::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)
是std::memory_model_acquire了负载运行的多余?是否可以通过将它们切换到进一步放宽加载和存储操作std::memory_order_relaxed?在这种情况下,获取/释放语义是否std::mutex足以保证其正确性,或者std::atomic_thread_fence(std::memory_order_release)还需要确保构造函数的内存写入在轻松存储之前发生?然而,使用栅栏相当于有商店memory_order_release吗?
编辑:感谢John的回答,我提出了以下应该是数据竞争的实现.尽管内部负载可能完全是非原子的,但我决定放弃一个宽松的负载,因为它不会影响性能.与总是具有获取存储器顺序的外部负载相比,thread_local机器提高了访问大约一个数量级的实例的性能.
static Tp &
instance()
{
static thread_local Tp *instance;
if (!instance &&
!(instance = m_instance.load(std::memory_order_acquire)))
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!(instance = m_instance.load(std::memory_order_relaxed)))
{
instance = new Tp;
m_instance.store(instance, std::memory_order_release);
}
}
return …Run Code Online (Sandbox Code Playgroud) 有没有办法在C++中实现单例对象:
(我不太了解我的C++,但是在执行任何代码之前初始化积分和常量静态变量的情况(即,甚至在执行静态构造函数之前 - 它们的值可能已在程序中"初始化")如果是这样的话 - 也许这可以被利用来实现单例互斥体 - 这反过来可以用来保护真正的单例的创建......)
很好,现在我似乎有几个好的答案(羞耻我不能标记2或3作为答案).似乎有两个广泛的解决方案:
c++ singleton construction multithreading lazy-initialization
我想在Java中实现多线程的延迟初始化.
我有一些类似的代码:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
Helper h;
synchronized(this) {
h = helper;
if (h == null)
synchronized (this) {
h = new Helper();
} // release inner synchronization lock
helper = h;
}
}
return helper;
}
// other functions and members...
}
Run Code Online (Sandbox Code Playgroud)
而且我得到了"Double-Checked Locking is Broken"声明.
我怎么解决这个问题?
为什么模式被认为是破碎的?它看起来很好吗?有任何想法吗?
public static Singleton getInst() {
if (instace == null) createInst();
return instace;
}
private static synchronized createInst() {
if (instace == null) {
instace = new Singleton();
}
}
Run Code Online (Sandbox Code Playgroud) Checkstyle将此代码报告为"双重检查锁定习语已损坏",但我认为我的代码实际上并未受到双重检查锁定问题的影响.
如果不存在具有该id的行,则该代码应该在数据库中创建一行.它在多线程环境中运行,我想避免主键存在的SQL异常.
伪代码:
private void createRow(int id) {
Row row = dao().fetch(id);
if (row == null) {
synchronized (TestClass.class) {
row = dao().fetch(id);
if (row == null) {
dao().create(id);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以同意它看起来像双重检查锁定,但我没有使用静态变量,fetch()和create()中的代码可能太复杂,无法内联并使其无序.
我错了还是格式?:)
从发行说明中,checkstyle删除了Double-Checked锁定检查.
我很难理解为什么.他们不仅在发布说明中回复了这个问题,而且还在问题跟踪器中回复:
删除DoubleCheckedLocking检查,如在Java 5(及更高版本)中,使用volatile关键字解决问题.
我假设,如果checkstyle正在删除此警告,那是因为它不再有用.也就是说,错误将不再发生或另一个警告完成工作.但
我不明白为什么在Java 5中不再发生这样的错误,或者它是如何被另一个警告补充的.有人可以解释一下吗?
编辑:我理解添加volatile关键字如何解决问题.我担心的是:这个警告不是值得的吗?我正在考虑程序员使用上述锁定模式的情况,但忘记声明变量volatile.checkstyle还不应警告它吗?
java ×4
singleton ×3
c++ ×2
concurrency ×2
locking ×2
atomic ×1
c# ×1
c++11 ×1
checkstyle ×1
construction ×1
lazy-loading ×1
volatile ×1