Реализация одноэлементного класса с атомами c членов - PullRequest
0 голосов
/ 04 апреля 2020

Я пытаюсь реализовать класс для отслеживания производительности многопоточного кода и сталкиваюсь со следующей проблемой:

У трекера должен быть один экземпляр, то есть шаблон синглтона. Чтобы посчитать создание объектов и выполнение функций по всем потокам, я подумал, что было бы неплохо использовать элемент atomi c. Но я не могу понять правильную реализацию.

Это минимальный код того, что я хочу сделать:

#include <atomic>
#include <iostream>

class Counter
{
public:
    static Counter& instance()
    {
        return instance_;
    };

    void increment()
    {
        counter_++;
    };

private:
    Counter ()
    {
        std::cout << "ctor counter" << std::endl;
    };

    ~Counter ()
    {
        std::cout << "counter_: " << counter_ << std::endl;
        std::cout << "dtor counter" << std::endl;
    }

    static Counter instance_;
    std::atomic<int> counter_{0};
    //int counter_ = 0;
};

Counter Counter::instance_ = Counter();

int main(void)
{
    Counter::instance().increment();
    Counter::instance().increment();
    Counter::instance().increment();

    return 0;
}

Если переменная counter_ равна int, он работает нормально, но не будет потокобезопасным. Если это atomic<int>, то компилятор сообщает мне следующее:

   g++ foo.cc
foo.cc:34:38: error: use of deleted function 'Counter::Counter(const Counter&)'
 Counter Counter::instance_ = Counter();
                                      ^
foo.cc:4:7: note: 'Counter::Counter(const Counter&)' is implicitly deleted because the default definition would be ill-formed:
 class Counter
       ^~~~~~~
foo.cc:4:7: error: use of deleted function 'std::atomic<int>::atomic(const std::atomic<int>&)'
In file included from foo.cc:1:0:
/usr/include/c++/7/atomic:668:7: note: declared here
       atomic(const atomic&) = delete;
       ^~~~~~

Я не уверен, что полностью понимаю проблему. Любое объяснение / решение будет высоко ценится.

Приветствия

1 Ответ

2 голосов
/ 04 апреля 2020

Вы могли бы упростить, если бы std::atomic<int> counter_{0}; был просто членом класса static вместо части каждого экземпляра. (Так как вы гарантируете, что существует только один экземпляр класса.)

Или, если вы используете только этот «одиночный» способ, возвращая ссылку на объект stati c, просто сделайте все его members static, поэтому вам даже не нужно получать указатель на этот единственный его экземпляр. Тогда это может быть просто прославленный namespace{} с публичными c и частными функциями-членами c. Я думаю, что единственная цель «синглтона» - отложить инициализацию до тех пор, пока не будет выполнена инициализация stati c, с использованием stati c с функциональной областью с неконстантным инициализатором, но вы этого не делаете.


Фактическая проблема в конструкторе копирования вашего класса, который ваш инициализатор stati c использует так, как вы его написали. Создает временную Counter(); и затем копирует ее в переменную stati c instance_.

Вы можете скомпилировать как C ++ 17, где исключение этой копии гарантировано ( работает на Godbolt с g ++ -std=gnu++17 с вашим нетронутым ), или вы можете переписать инициализатор

Counter Counter::instance_;    // default construct
Counter Counter::instance_{};  // explicitly default construct

Оба эти с g ++ -std=gnu++11

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...