Выбрать главу

6.3.4. Многопоточная работа

В исходной реализации в каждом потоке мы могли создать свой экземпляр класса ISensorControl и работать с ним независимо. В случае API это не работает, потому что экземпляр класса в реализации интерфейса объявляется глобальным, и все интерфейсные функции обращаются к одному и тому же экземпляру класса. Выходом здесь будет выделение отдельной области памяти для экземпляра класса в рамках одного потока, т. е. использование локальной памяти потока.

До появления стандарта C++ 11 использовать локальную память потока было непросто: для этого требовалось явное обращение к функциям операционной системы, что усложняло реализацию и делало код платформенно-зависимым. В C++ 11 появилось ключевое слово thread_local, и это сильно упростило жизнь: если в объявлении переменной добавить указанный спецификатор, то она становится локальной в рамках потока, т. е. каждый новый создаваемый поток будет иметь независимый экземпляр соответствующей переменной. Таким образом, достаточно экземпляр интерфейсного класса ISensorControl объявить как thread_local, и теперь для каждого потока будет существовать отдельный независимый экземпляр класса (Листинг 106).

Листинг 106. Объявление экземпляра класса как локального для текущего выполняемого потока (SensorLib.cpp)

using ControlPointer = std::unique_ptr<sensor::ISensorControl>;

thread_local ControlPointer g_SensorControl(sensor::ISensorControclass="underline" :createControl());

6.3.5. Настройка драйвера

В исходной реализации в начале работы мы создавали необходимый класс драйвера, который затем передавали интерфейсному классу (Листинг 107). Но в интерфейсах системных API мы классы использовать не можем, как поступить в этом случае? Можно предложить следующее решение: класс драйвера создавать внутри API, а в функцию настройки передавать идентификатор, в соответствии с которым будет создан соответствующий драйвер (Листинг 108).

Листинг 107. Настройка драйвера в исходной реализации

ISensorControl sensorControl = ISensorControclass="underline" :createControl;

DriverPointer driver = IDriver::createDriver(DRIVER_SIMULATION);

driver->initialize();

sensorControl->assignDriver(driver);

Листинг 108. Настройка драйвера в системном API (SensorLib.h)

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 объявлена вспомогательная функция для создания драйвера. Эта функция проверяет, создан ли драйвер соответствующего типа, при необходимости создает, инициализирует и передает его в интерфейсный класс.