Как запретить нескольким потокам использовать экземпляр класса singleton одновременно в cpp - PullRequest
0 голосов
/ 04 января 2019

Я пишу потокобезопасный одноэлементный класс следующим образом. Следующая реализация обеспечивает создание только одного экземпляра класса. Мой вариант использования заключается в том, что я использую этот экземпляр в многопоточной среде, где каждый поток может вызвать getInstance() и выполнить некоторую работу с использованием экземпляра. Мой вопрос заключается в том, как я могу убедиться, что только один поток использует экземпляр в определенное время, чтобы предотвратить условия гонки, которые могут возникнуть, если несколько потоков пытаются использовать один экземпляр одновременно.

class Singleton {
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
 public:
    static Singleton& getInstance() {
        static Singleton s;
        return s;
    }
};

Ответы [ 2 ]

0 голосов
/ 04 января 2019

У меня вопрос, как я могу убедиться, что только один поток использует экземпляр в определенное время.

Как правило , вы не можете.

Ответ Натана Оливера работает в особом случае, когда другие модули могут «использовать экземпляр» только путем вызова его методов. В этом случае вы можете убедиться, что каждый из этих методов блокирует один и тот же мьютекс.

Но если ваш синглтон предоставляет какой-либо элемент данных public, все ставки отключены: у вас не будет никакого способа контролировать, как другие модули "используют" открытые элементы данных вашего объекта.


Осторожно! Даже если вы сделаете все члены данных частными и сделаете объект-одиночку действительно, действительно «поточно-ориентированным»; все же не гарантирует безопасность потоков другого кода, которая может зависеть от некоторых отношений между вашим одноэлементным объектом и некоторыми другими данными.

Безопасность потоков на самом деле не означает, что «только один поток использует объект одновременно». Потокобезопасность заключается в сохранении инвариантных отношений . Например, если у вас есть структура данных с двойным кольцом , то важным инвариантом является то, что p->next->prev всегда должно быть равно p.

Поток, который хочет склеить новый элемент в кольцо, должен временно сломать инвариант. «Безопасность потока» в этом случае означает, что никакой другой поток не сможет увидеть временно прерванное состояние.

Создание программы из «поточно-ориентированных» объектов не делает всю программу «поточно-ориентированной», поскольку программа более высокого уровня может зависеть от важных инвариантных отношений между объектами. И, хотя объекты являются индивидуально потокобезопасными, у них нет возможности узнать о связях более высокого уровня, которые имеют значение для программы в целом.

0 голосов
/ 04 января 2019

Одна вещь, которую вы можете сделать, это просто сделать все ее члены потокобезопасными, заблокировав мьютекс.

class Singleton {
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    std::mutex my_mutex_member;
 public:
    static Singleton& getInstance() {
        static Singleton s;
        return s;
    }
    void my_singleton_cool_function()
    {
        std::lock_guard<std::mutex> lg(my_mutex_member);
        // cool code here
    }
};

В приведенном выше примере lg заблокирует мьютекс и в конце функции,когда lg будет уничтожен, деструктор разблокирует мьютекс.Это означает, что только один поток может запустить функцию одновременно.Это позволяет всем потокам иметь ссылку на синглтон и будет блокироваться, только если два или более потока попытаются сделать одно и то же одновременно.

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