Является ли разумным заменить boost:: thread и boost:: mutex на С++ 11 эквивалентов?
Мотивация: причина, по которой я рассматриваю это, заключается в том, что мой гениальный менеджер проекта думает, что повышение - это еще одна зависимость, и что это ужасно, потому что "вы зависите от этого" (я попытался объяснить качество повышения, а затем сдался после некоторых time:(). Меньшая причина, по которой я хотел бы это сделать, это то, что я хотел бы изучить возможности С++ 11, потому что люди начнут писать код в нем.
Итак:
- Существует ли отображение 1:1 между
#include<thread> #include<mutex>
и
повышающие эквиваленты?
- Вы считаете хорошей идеей заменить boost с помощью С++ 11
вещи. Мое использование примитивно, но есть примеры, когда std doesnt
предложите, что толчок? Или (богохульство) наоборот?
P.S.
Я использую GCC, поэтому есть заголовки.
Ответы
Ответ 1
Существует несколько различий между Boost.Thread и стандартной библиотекой потоков С++ 11:
- Boost поддерживает отмену потока, потоки С++ 11 не
- С++ 11 поддерживает
std::async
, но Boost не
- Boost имеет
boost::shared_mutex
для блокировки с несколькими считывателями/одиночными авторами. Аналогичный std::shared_timed_mutex
доступен только с С++ 14 (N3891), а std::shared_mutex
доступен только с С++ 17 (N4508).
- Тайм-ауты С++ 11 отличаются от тайм-аутов Boost (хотя это скоро должно измениться теперь Boost.Chrono было принято).
- Некоторые имена различаются (например,
boost::unique_future
vs std::future
)
- Семантика аргумента
std::thread
отличается от boost::thread
- Boost использует boost::bind
, для чего требуются скопируемые аргументы. std::thread
позволяет передавать только типы перемещения, такие как std::unique_ptr
, как аргументы. Из-за использования boost::bind
семантика заполнителей, таких как _1
во вложенных выражениях связей, также может быть разной.
- Если вы явно не вызываете
join()
или detach()
, тогда оператор деструктора и назначения boost::thread
вызовет detach()
на объект потока, который будет уничтожен/назначен. С объектом С++ 11 std::thread
это приведет к вызову std::terminate()
и прервет работу приложения.
Чтобы прояснить суть параметров только для перемещения, допустимо С++ 11 и переносит право собственности на int
из временного std::unique_ptr
на параметр f1
при запуске нового потока, Однако, если вы используете boost::thread
, тогда он не будет работать, поскольку он использует boost::bind
внутри, а std::unique_ptr
не может быть скопирован. Также существует ошибка в библиотеке потоков С++ 11, предоставляемой с помощью GCC, которая мешает этой работе, поскольку она также использует std::bind
в реализации.
void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));
Если вы используете Boost, вы можете, вероятно, переключиться на потоки С++ 11 относительно безболезненно, если ваш компилятор поддерживает его (например, последние версии GCC на linux имеют в основном полную реализацию библиотеки потоков С++ 11, доступной в -std=c++0x
).
Если ваш компилятор не поддерживает потоки С++ 11, вы можете получить стороннюю реализацию, такую как Just:: Thread, но это все еще является зависимостью.
Ответ 2
std::thread
в значительной степени моделируется после boost::thread
, несколько различий:
- форматировать не скопируемые, одно-ручные-карты-к-одному-os-thread, семантика сохраняется. Но этот поток является подвижным, чтобы позволить возвращать поток из функций factory и помещать в контейнеры.
- Это предложение добавляет отмену
boost::thread
, что является значительным осложнением. Это изменение оказывает большое влияние не только на поток, но и на остальную библиотеку потоков С++. Считается, что это большое изменение оправдано из-за выгоды. - Теперь деструктор потока должен отменить отмену перед отсоединением, чтобы избежать случайного утечки дочерних потоков при отмене родительских потоков.
- Теперь требуется явный элемент отсоединения, чтобы включить отключение без отмены.
- Понятия дескриптора потока и идентичности потоков были разделены на два класса (они являются одним и тем же классом в
boost::thread
). Это упрощает управление и хранение идентификаторов потоков. - Добавлена возможность создания идентификатора потока, который, как гарантируется, сравнивается с любым другим соединяемым потоком (
boost::thread
не имеет этого). Это удобно для кода, который хочет знать, выполняется ли он тем же потоком, что и предыдущий вызов (рекурсивные мьютексы являются конкретным примером). - Существует "задняя дверь", чтобы получить дескриптор собственного потока, чтобы клиенты могли манипулировать потоками с использованием базовой ОС, если это необходимо.
Это с 2007 года, поэтому некоторые пункты больше не действительны: boost::thread
теперь имеет функцию native_handle
, и, как отмечают комментаторы, std::thread
больше не отменяет.
Я не мог найти существенных различий между boost::mutex
и std::mutex
.
Ответ 3
Есть одна причина не переходить на std::thread
.
Если вы используете статическое связывание, std::thread
становится непригодным для использования из-за следующих ошибок/возможностей gcc:
А именно, если вы вызовете std::thread::detach
или std::thread::join
это приведет либо к исключению, либо к сбою, тогда как boost::thread
работает нормально в этих случаях.
Ответ 4
Корпоративный случай
Если вы пишете программное обеспечение для предприятия, которое должно работать на умеренных или больших операционных системах и, следовательно, собирать с различными компиляторами и версиями компиляторов (особенно относительно старых) для этих операционных систем, я советую вам избегать С++ 11 в целом на данный момент. Это означает, что вы не можете использовать std::thread
, и я бы рекомендовал использовать boost::thread
.
Базовый /Tech Startup Case
Если вы пишете для одной или двух операционных систем, вы наверняка знаете, что вам когда-либо понадобится строить только с помощью современного компилятора, который в основном поддерживает С++ 11 (например, VS2015, GCC 5.3, Xcode 7), и вы этого еще не сделали в зависимости от библиотеки наддува, std::thread
может быть хорошим вариантом.
Мой опыт
Я лично неравнодушен к закаленным, интенсивно используемым, высоко совместимым, высокосогласованным библиотекам, таким как boost по сравнению с очень современной альтернативой. Это особенно верно для сложных предметов программирования, таких как многопоточность. Кроме того, я уже давно добился большого успеха с boost::thread
(и boost в целом) в широком спектре сред, компиляторов, потоковых моделей и т.д. Когда это мой выбор, я выбираю boost.
Ответ 5
С Visual Studio 2013 std::mutex
, похоже, ведет себя иначе, чем boost::mutex
, что вызвало у меня некоторые проблемы (см. этот вопрос).
Ответ 6
Что касается std :: shared_mutex, добавленного в С++ 17
Другие ответы здесь дают очень хороший обзор различий в целом. Однако есть несколько проблем с std::shared_mutex
которые буст решает.
-
Восстанавливаемые мутанты. Они отсутствуют в std::thread
. Они позволяют преобразовать читателя в писателя, не позволяя другим писателям входить до вас. Они позволяют выполнять такие вещи, как предварительная обработка больших вычислений (например, переиндексация структуры данных) в режиме чтения, а затем обновляться до записи, чтобы применить переиндекс, удерживая блокировку записи только в течение короткого времени.
-
Честность. Если у вас есть постоянная активность чтения с помощью std::shared_mutex
, ваши авторы будут заблокированы на неопределенный срок. Это потому, что если придет другой читатель, им всегда будет отдан приоритет. С boost:shared_mutex
все потоки в конечном итоге получат приоритет. (1) Ни читатели, ни писатели не будут голодать.
Суть этого в том, что если у вас система с очень высокой пропускной способностью, без простоев и с высокой конкуренцией, std::shared_mutex
никогда не будет работать для вас без ручной сборки системы приоритетов поверх нее. boost::shared_mutex
будет работать из коробки, хотя в некоторых случаях вам, возможно, придется поработать с ним. Я бы сказал, что поведение std::shared_mutex
- это скрытая ошибка, ожидающая своего появления в большинстве кодов, которые его используют.
(1)Фактический алгоритм, который он использует, основан на планировщике потоков ОС.По моему опыту, когда чтение насыщено, паузы (при получении блокировки записи) в Windows длиннее, чем в OSX/Linux.
Ответ 7
Я попытался использовать shared_ptr из std вместо boost, и я действительно нашел ошибку в gcc-реализации этого класса. Мое приложение рушилось из-за двойного вызова деструктора (этот класс должен быть потокобезопасным и не должен генерировать такие проблемы). После перехода на boost:: shared_ptr все проблемы исчезли. Текущие реализации С++ 11 все еще не зрелые.
У Boost есть еще больше возможностей. Например, заголовок в версии std не предоставляет сериализатор потоку (т.е. Cout < продолжительность). У Boost есть много библиотек, которые используют свои собственные и т.д. Эквиваленты, но не взаимодействуют со стандартными версиями.
Подводя итог - если у вас уже есть приложение, написанное с использованием boost, безопаснее хранить ваш код, как он есть, вместо того чтобы приложить некоторые усилия при переходе к стандарту С++ 11.