воскресенье, 24 июня 2018 г.

C++: thread pool, lambda и семантика копирования

Пулы тредов - один из наиболее часто используемых паттернов в программировании на C++. И, к сожалению, очень часто он используется неправильно.

Знаете ли вы, например, что пул тредов можно очень легко по незнанию или неопытности сериализовать, сведя на нет его достоинства?

Многие примеры в интернете показывают, как пулы тредов сочетают, например, с future. Конкретно - с packaged task. Который имеет семантику move, и, ввиду передачи параметров по ссылке и области видимости, может в одно действие сериализовать пул тредов целиком.


Обратная сторона медали - обработка пулом тредов мало-мальски крупных данных. Например, больших строк. Для правильной работы пула их необходимо передавать по значению. Однако передача строк по значению просаживает производительность, так как их необходимо посимвольно копировать. К сожалению, других вариантов просто нет.

Взгляните на код:
      tp::ThreadPoolOptions options;  
      options.setThreadCount(v_threads);  
      options.setQueueSize(v_queue);  
      tp::ThreadPool pool(options);  
   
      std::string v_in_stream;  
      while (std::getline(std::cin, v_in_stream)) {  
           pool.post(([=]() { processdata(v_in_stream); }));  
      }   

Вам необходимо в цикле заполнять пул для обработки строковых значений, непрерывно поступающих на STDIN. Здесь библиотека пула содержит очередь, в которую лямбда ставит вызовы обрабатывающей процедуры с переданным аргументом, таким образом, значание, полученное из цикла, должно в принципе сохраняться до завершения выполнения processdata(). Если передавать значение в лямбду по ссылке ([&] вместо [=], задано явно, с целью самодокументирования кода), то пул работает некорректно. Так как область видимости указателя на v_in_stream заканчивается вызовом pool.post(). Здесь вообще появляется UB. Чтобы добиться корректной работы, можно использовать packaged task, но это в принципе сериализует работу пула, как я и говорил. Выполнение будет асинхронным, но не многопоточным.