2015-03-04 2 views
1

Я попытался реализовать очень простой текстовый класс Local Singleton в C++ - это класс шаблонов, который наследуют другие классы. Проблема заключается в том, что он почти всегда работает, но и теперь каждый раз (скажем, 1 работать в 15), она не будет выполнена с ошибкой вдоль линий:C++ Thread Локальный одноточечный прерывистый сбой

* GLibC обнаружен * ./myExe: бесплатно (): недопустимый следующий размер (быстрый): 0x00002b61a40008c0 ***

Прошу простить пример, надуманный на примере, но он служит для демонстрации проблемы.

#include <thread> 
#include <atomic> 
#include <iostream> 
#include <memory> 
#include <vector> 

using namespace std; 

template<class T> 
class ThreadLocalSingleton 
{ 
public: 
    /// Return a reference to an instance of the object 
    static T& instance(); 

    typedef unique_ptr<T> UPtr; 

protected: 
    ThreadLocalSingleton() {} 
    ThreadLocalSingleton(ThreadLocalSingleton const&); 
    void operator=(ThreadLocalSingleton const&); 
}; 

template<class T> 
T& ThreadLocalSingleton<T>::instance() 
{ 
    thread_local T m_instance; 
    return m_instance; 
} 

// Create two atomic variables to keep track of the number of times the 
// TLS class is created and accessed. 
atomic<size_t> creationCount(0); 
atomic<size_t> accessCount(0); 

// Very simple class which derives from TLS 
class MyClass : public ThreadLocalSingleton<MyClass> 
{ 
    friend class ThreadLocalSingleton<MyClass>; 
public: 
    MyClass() 
    { 
     ++creationCount; 
    } 

    string getType() const 
    { 
     ++accessCount; 
     return "MyClass"; 
    } 
}; 

int main(int,char**) 
{ 
    vector<thread> threads; 
    vector<string> results; 

    threads.emplace_back([&]() { results.emplace_back(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&]() { results.emplace_back(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&]() { results.emplace_back(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&]() { results.emplace_back(MyClass::instance().getType()); MyClass::instance().getType(); }); 

    for (auto& t : threads) 
    { 
     t.join(); 
    } 

    // Expecting 4 creations and 8 accesses. 
    cout << "CreationCount: " << creationCount << " AccessCount: " << accessCount << endl; 
} 

Я могу повторить это на coliru, используя команду сборки: г ++ -std = C++ 11 -O2 -Wall -pedantic -pthread main.cpp & & ./a.out

Большое спасибо!

+3

Возможно, у вас есть одновременная модификация «результатов». – molbdnilo

+1

Реализация синглтона как такового кажется в основном ОК ('thread_local' подразумевает static', а' static' locals гарантированно инициализируются поточно-безопасными). Что нельзя сказать о доступе к «вектору» одновременно. Хотя об одном синглете интересно, зачем нужен ниток-локальный синглтон в первую очередь (либо это одноэлемент, либо один в потоке?), И я предпочел бы '= delete' конструктор копирования и оператор присваивания. – Damon

+0

Конечно, это наиболее вероятная проблема, emplace_back не является потокобезопасным. Я проверю, будет ли исправление, повышающее надежность. Спасибо, парни :) – DaveM

ответ

1

Благодаря как мольбднило, так и Деймону, который быстро указал на очевидное - вектор :: emplace_back не является потокобезопасным, поэтому не было бы гарантий относительно того, действительно ли этот код будет работать. Я заменил функцию main() следующим, что кажется более надежным.

int main(int,char**) 
{ 
    vector<thread> threads; 
    vector<string> results; 

    auto addToResult = [&results](const string& val) 
    { 
     static mutex m_mutex; 
     unique_lock<mutex> lock(m_mutex); 
     results.emplace_back(val); 
    }; 

    threads.emplace_back([&addToResult]() { addToResult(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&addToResult]() { addToResult(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&addToResult]() { addToResult(MyClass::instance().getType()); MyClass::instance().getType(); }); 
    threads.emplace_back([&addToResult]() { addToResult(MyClass::instance().getType()); MyClass::instance().getType(); }); 

    for (auto& t : threads) 
    { 
     t.join(); 
    } 

    // Expecting 4 creations and 8 accesses. 
    cout << "CreationCount: " << creationCount << " AccessCount: " << accessCount << endl; 
} 

Спасибо!

Смежные вопросы