6.3.4. Многопоточная работа
В исходной реализации в каждом потоке мы могли создать свой экземпляр класса ISensorControl и работать с ним независимо. В случае API это не работает, потому что экземпляр класса в реализации интерфейса объявляется глобальным, и все интерфейсные функции обращаются к одному и тому же экземпляру класса. Выходом здесь будет выделение отдельной области памяти для экземпляра класса в рамках одного потока, т. е. использование локальной памяти потока.
До появления стандарта C++ 11 использовать локальную память потока было непросто: для этого требовалось явное обращение к функциям операционной системы, что усложняло реализацию и делало код платформенно-зависимым. В C++ 11 появилось ключевое слово thread_local, и это сильно упростило жизнь: если в объявлении переменной добавить указанный спецификатор, то она становится локальной в рамках потока, т. е. каждый новый создаваемый поток будет иметь независимый экземпляр соответствующей переменной. Таким образом, достаточно экземпляр интерфейсного класса ISensorControl объявить как thread_local, и теперь для каждого потока будет существовать отдельный независимый экземпляр класса (Листинг 106).
using ControlPointer = std::unique_ptr<sensor::ISensorControl>;
thread_local ControlPointer g_SensorControl(sensor::ISensorControclass="underline" :createControl());
6.3.5. Настройка драйвера
В исходной реализации в начале работы мы создавали необходимый класс драйвера, который затем передавали интерфейсному классу (Листинг 107). Но в интерфейсах системных API мы классы использовать не можем, как поступить в этом случае? Можно предложить следующее решение: класс драйвера создавать внутри API, а в функцию настройки передавать идентификатор, в соответствии с которым будет создан соответствующий драйвер (Листинг 108).
ISensorControl sensorControl = ISensorControclass="underline" :createControl;
DriverPointer driver = IDriver::createDriver(DRIVER_SIMULATION);
driver->initialize();
sensorControl->assignDriver(driver);
thread_local sensor::DriverPointer g_DriverSimulation; // (1)
thread_local sensor::DriverPointer g_DriverUSB; // (2)
thread_local sensor::DriverPointer g_DriverEthernet; // (3)
void CreateDriver(sensor::DriverType driverType, sensor::DriverPointer& driverPointer) // (4)
{
if (!driverPointer)
{
driverPointer = sensor::IDriver::createDriver(driverType);
driverPointer->initialize();
}
g_SensorControl->assignDriver(driverPointer);
}
ErrorCode assignDriver(DriverType driverType) // (5)
{
ErrorCode error = ERROR_NO;
try
{
EnumConverter<sensor::DriverType> conv;
conv.convert (driverType,
{sensor::DriverType::Simulation, sensor::DriverType::Usb, sensor::DriverType::Ethernet}); // (6)
if (conv.error())
{
return ERROR_INVALID_ARGUMENT;
}
switch (conv.result()) // (7)
{
case sensor::DriverType::Simulation:
{
CreateDriver(sensor::DriverType::Simulation, g_DriverSimulation);
}
break;
case sensor::DriverType::Usb:
{
CreateDriver(sensor::DriverType::Usb, g_DriverUSB);
}
break;
case sensor::DriverType::Ethernet:
{
CreateDriver(sensor::DriverType::Ethernet, g_DriverEthernet);
}
break;
}
}
catch (sensor::sensor_exception& e)
{
error = static_cast<ErrorCode>(e.code());
}
return error;
}
В строках 1–3 объявляются указатели для хранения классов всех возможных типов драйверов. В строке 4 объявлена вспомогательная функция для создания драйвера. Эта функция проверяет, создан ли драйвер соответствующего типа, при необходимости создает, инициализирует и передает его в интерфейсный класс.