У меня есть класс со всеми статическими методами, как это:
class A {
public:
static std::string getA() { GlobalData::alfa; }
static std::string sum(int x, int y) { ... }
static int convert() { ... }
};
Мне нужно, чтобы A мог быть потокобезопасным. Какой дизайн лучше для этого? Мне нужно преобразовать все методы в нестатический метод, как это?
class B {
public:
std::string getA() { g.alfa; }
std::string sum(int x, int y) { ... }
int convert() { ... }
private:
GlobalData g;
};
Учтите, что GlobalData — это простой POD, подобный этому:
struct GlobalData
{
static std::string foo;
static int bar;
...
}
Вы можете сохранить оригинальный макет класса A
или даже вместо этого измените его на пространство имен, но вам придется определить GlobalData
struct как локальное хранилище потока, если содержащиеся в нем данные должны быть специфическими для каждого потока:
struct GlobalData {
static thread_local std::string alfa;
// other members here
};
Вам, вероятно, потребуется вызвать функцию для инициализации данных, необходимых для каждого потока.
Обратите внимание, что вы также можете превратить эту структуру в пространство имен, если все члены были определены static
:
namespace GlobalData {
thread_local std::string alfa;
// etc.
}
namespace A {
std::string getA() { return GlobalData::alfa; }
std::string sum(int x, int y) { /* ... */ }
int convert() { /* ... */ }
}
что улучшает читабельность вашего кода.
Это же правило должно применяться к любому фрагменту данных глобальной области видимости в вашем исходном коде, который должен стать специфичным для потока.
Лучше было бы не использовать A в статической реализации вообще. Это будет означать, что вы создаете экземпляр A и используете его в клиентском коде посредством внедрения зависимостей.
Затем вы можете реализовать доступ RW к данным A, используя стандартные примитивы синхронизации.
Поскольку ваш A имеет статическое состояние, а A не является внедренной зависимостью, код, использующий A внутри, будет синхронизироваться с примитивами синхронизации, которые вы хотите добавить в реализацию A. Это вводит потенциальные взаимоблокировки, которые полностью невидимы из клиентского кода, которые может быть трудно найти и диагностировать (в зависимости от сложности взаимодействий клиентского кода A).