C++ Thread Local Синглтон прерывистый сбой

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

* Обнаружен glibc *./myExe: free (): недействительный следующий размер (быстрый): 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, используя команду build: g++ -std= C++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

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

1 ответ

Спасибо как molbdnilo, так и Damon, которые быстро указали на очевидное - vector::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;
}

Спасибо!

Другие вопросы по тегам