C ++ 11 Singleton。静态变量是线程安全的吗?为什么?

时间:2015-12-24 20:03:25

标签: c++11

我刚才读到,这个结构:

NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSString *writableDbtPath = [documentsDirectory stringByAppendingPathComponent:@"NimrokhRavanshenasi.sqlite3"];

BOOL success = [fileManager fileExistsAtPath:writableDbtPath];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"NimrokhRavanshenasi.sqlite3"];
NSError *error;
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDbtPath error:&error];


NSFileManager *filemgr = [NSFileManager defaultManager];

sqlite3_stmt *compiledStatement;
if([filemgr fileExistsAtPath:writableDbtPath]){
    if (sqlite3_open([writableDbtPath UTF8String], &_contactDB) != SQLITE_OK) {
        NSLog(@"Failed to open database!");

是一种创建单例的线程安全且有效的方法吗?!我是否正确,每次调用此方法时,静态AppSettings变量都是相同的?!我对这个问题的范围有点困惑......

我的正常做法是使用unique_ptr作为我班级的静态成员......但这似乎有用......有人可以向我解释一下,这里发生了什么?!

顺便说一下:const在这里有意义吗?!

2 个答案:

答案 0 :(得分:10)

在C ++ 11(和转发版)中,函数local static AppSettings的构造保证是线程安全的。注意:在VS-2015之前,Visual Studio没有实现C ++ 11的这个方面。

编译器将在AppSettings旁边放置一个隐藏标志,指示它是否为:

  • 未构建。
  • 正在建造。
  • 已建成。

第一个线程将找到设置为“not construct”的标志并尝试构造该对象。成功构建后,标志将被设置为“已构建”。如果另一个线程出现并且发现标志设置为“正在构造”,它将等待该标志设置为“构造”。

如果构造因异常而失败,则该标志将被设置为“not construct”,并且将在下一次传递时重试构造(在同一个线程或不同的线程上)。

对象instance将继续为您的计划的其余部分构建,直到main()返回,此时instance将被销毁。

每次执行任何线程都通过AppSettings::GetInstance()时,它将引用完全相同的对象。

在C ++ 98/03中,不保证构造是线程安全的。

如果AppSettings的构造函数以递归方式输入AppSettings::GetInstance(),则行为未定义。

如果编译器可以在编译时看到如何构造instance“,则允许它。

如果AppSettings具有constexpr构造函数(用于构造instance的构造函数),并且instance符合constexpr,则编译器是必需的在编译时构造instance。如果在编译时构造instance,则“not-construct / construct”标志将被优化掉。

答案 1 :(得分:3)

您的代码行为类似于:

namespace {
    std::atomic_flag initialized = ATOMIC_FLAG_INIT;
    std::experimental::optional<bg::AppSettings> optional_instance;
}

const bg::AppSettings& bg::AppSettings::GetInstance()
{
    if (!initialized.test_and_set()) {
        optional_instance.emplace();
    }
    return *optional_instance;
}

通过在程序的整个持续时间内生成一个线程安全标志,编译器可以在每次调用函数时检查此标志,并仅初始化变量一次。一个真正的实现可以使用其他机制来获得同样的效果。