Безопасно ли передавать структуру с использованием конструктора перемещения по умолчанию и оборачивать ее в shared_ptr в лямбда-функцию, выполняемую в tbb :: task? - PullRequest
0 голосов
/ 19 сентября 2019

Контекст: В одном из моих приложений на C ++ 11 сериализация объектов и публикация сообщений занимают много времени.Поэтому я хочу сделать это в отдельном потоке, используя библиотеку Intel TBB (более конкретно, используя tbb :: task_group )

Issue : объект для сериализации являетсяструктура, в которой некоторые свойства std::vector<std::unique_ptr<SomeObject>>, что делает невозможным передачу копии в лямбду, выполненную в задаче

Примерно это выглядит как

struct MockedDC {
  MockedDC(int x, std::vector<std::unique_ptr<SomeObject>> v) : x(x),
                                                                v(std::move(v)) {};

  int                                      x;
  std::vector<std::unique_ptr<SomeObject>> v;
};

"Решение", которое я нашел, должен восстановить в куче конструктор перемещения моего экземпляра и обернуть его в shared_ptr<MockedDC>, который можно копировать.В конце концов, функция, которая вызывает tbb::task_group::run, выглядит как

// function called like this `executeInThread(taskGroup, std::move(mockedDC))`

void  executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
  const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
  auto f = [sharedMockedDC] {
    const auto serialized(serializer(*sharedMockedDC)); // pass by reference
    publish(serialized);
  };

  taskGroup.run(f);
};

, она компилируется и работает нормально, но я не могу подвергнуть ее давлению, так как это будет в реальной жизни, поэтому мой вопрос это безопасно / разумно делать это?

Я нашел в другом stackoverflow вопрос альтернативы , но реализация выглядит сложной для поддержки, учитывая мойЗнание C ++ :), поэтому я хочу придерживаться подхода shared_ptr , как это было предложено где-то еще

Что я пробовал до сих пор: Я написал фиктивный код дляпроверить вещь, но я думаю, что этого недостаточно для проверки этого подхода.Я также хотел скомпилировать с некоторыми флагами очистки, но tbb не удалось связать с кучей ошибок, таких как undefined reference to __ubsan_handle_pointer_overflow

Вот пример фиктивного примера, если это помогает ответить (он компилируется и запускается без проблем (кроме некоторыхпереполнение int, но это не проблема, я думаю))

#include <cstdio>
#include <iostream>
#include <memory>
#include <vector>
#include <numeric>
#include "tbb/task_scheduler_init.h"
#include "tbb/task_group.h" 

struct MockedDC {
  MockedDC(int seed, size_t baseLen) : seed(seed), baseLen(baseLen) {
    this->a_MDC.reserve(baseLen);
    for (size_t i = 0; i < baseLen; ++i)
      this->a_MDC.emplace_back(new int((seed + i) / (seed + 1)));
    };

    int                               seed;
    size_t                            baseLen;
    std::vector<std::unique_ptr<int>> a_MDC;
  };

void  executeInThread(tbb::task_group& taskGroup, MockedDC mockedDC) {
  const std::shared_ptr<MockedDC> sharedMockedDC(new MockedDC(std::move(mockedDC)));
  auto f = [sharedMockedDC] {
    std::cout <<
      std::accumulate(sharedMockedDC->a_MDC.begin(), sharedMockedDC->a_MDC.end(), 0, [](int acc, const std::unique_ptr<int>& rhs) {
          return acc + *rhs;
      })
      << std::endl << std::flush;
  };
  taskGroup.run(f);
};


void  triggerTest(tbb::task_group& taskGroup) {
  for (size_t i = 0; i < 1000000; ++i) {
    MockedDC  mdc(i, 10000000);
    executeInThread(taskGroup, std::move(mdc));
  }
  return ;
};

int main() {
  tbb::task_scheduler_init  tbbInit(tbb::task_scheduler_init::automatic);
  //tbb::task_scheduler_init  tbbInit(8);
  tbb::task_group           taskGroup;

  triggerTest(taskGroup);
  taskGroup.wait();
  return (0);
};

PS: использование C ++ 14 нового захвата при перемещении не работает из-за библиотеки TBB: /

...