2

我对 C++ 中的单例和多线程编程有疑问以下您可以看到带有名为 shared 的变量的单例类的示例代码。

我创建了 1000 个线程来修改(+1)我的 Singleton 全局实例的该变量。shared 的最终值是 1000,但我希望这个值低于 1000,因为我没有保护这个变量的并发性。

代码真的是线程安全的,因为类是 Singleton 还是碰巧很幸运,值是 1000,但它完全可以小于 1000?

#include <iostream>
using namespace std;

class Singleton {
private:
    Singleton() {shared = 0;};
    static Singleton * _instance;
    int shared;

public:
    static Singleton* Instance();
    void increaseShared () { shared++; };
    int getSharedValue () { return shared; };
};

// Global static pointer used to ensure a single instance of the class.
Singleton* Singleton::_instance = NULL;

Singleton * Singleton::Instance() {
    if (!_instance) { 
        _instance = new Singleton;
    }

    return _instance;
}

void * myThreadCode (void * param) {
    Singleton * theInstance;
    theInstance = Singleton::Instance();
    theInstance->increaseShared();

    return NULL;
}

int main(int argc, const char * argv[]) {
    pthread_t threads[1000];
    Singleton * theInstance = Singleton::Instance();

    for (int i=0; i<1000; i++) {
        pthread_create(&threads[i], NULL, &myThreadCode, NULL);
    }

    cout << "The shared value is: " << theInstance->getSharedValue() << endl;

    return 0;
}
4

2 回答 2

5

代码真的是线程安全的,因为类是 Singleton 还是碰巧很幸运,值是 1000,但它完全可以小于 1000?

你好幸运啊...

实际上,您所观察到的最可能的问题与以下事实有关:在特定机器上增加单例值所需的时间少于操作系统分配资源所需的时间启动单个 pthread。因此,您永远不会遇到两个线程争夺单例的未受保护资源的情况。

更好的测试是首先启动所有pthread,让它们阻塞在屏障或条件变量上,然后在满足所有线程“活动”的屏障条件时对单例执行增量......那时,您将更有可能看到诸如增量操作之类的非原子操作发生的各种数据竞争。

于 2012-07-17T01:38:30.787 回答
1

如果你Singleton这样实现,单例创建将是线程安全的:

Singleton & Singleton::Instance() {
    static Singleton instance;
    return instance;
}

由于实例永远不能为空,并且管理器没有内存,因此返回引用而不是指针。

增量操作可以通过使用平台特定的操作(g++ 提供内置的,例如__sync_fetch_and_add),或来自 STL 的 C++11 原子,或 Boost.Atomic,或使用互斥锁来实现原子化。

std::atomic<int> shared;

void increaseShared () { ++shared; };
于 2012-07-17T02:06:02.197 回答