Разумно ли заменить boost :: thread и boost :: mutex на c ++ 11 эквивалентов?


153

Мотивация: причина, по которой я рассматриваю это, заключается в том, что мой гениальный руководитель проекта считает, что повышение - это еще одна зависимость, и это ужасно, потому что «вы зависите от этого» (я попытался объяснить качество повышения, а затем через некоторое время сдался :( Меньшая причина, по которой я хотел бы это сделать, заключается в том, что я хотел бы изучить возможности c ++ 11, потому что люди начнут писать код в нем. Итак:

  1. Есть ли соотношение 1: 1 между #include<thread> #include<mutex>эквивалентами и буст-эквивалентами?
  2. Считаете ли вы хорошей идеей заменить буст-контент на c ++ 11
    ? Я использую примитивно, но есть ли примеры, когда std не предлагает то, что делает boost? Или (богохульство) наоборот?

PS Я использую GCC, поэтому заголовки есть.


46
ИМО руководящие принципы кодирования Google глупы во многих отношениях ... Например. они не позволяют авто из C ++ 11 ... :)
NoSenseEtAl

5
Рекомендации по цитированию: [auto] затрудняет читабельность [потому что он удаляет] проверенную избыточность (например, имена типов), которая может быть полезна читателям.
Эндрю Томазос

31
for (auto it = v.begin () ... :)
NoSenseEtAl

15
@ AndrewTomazos-Fathomling: Действительно? Лично я не думаю, что когда-либо заботился о фактическом типе итератора (ну, может быть, несколько раз), только о поддерживаемых операциях ... Я бы сказал, что синтаксическая избыточность редко является хорошей идеей (DRY).
Гризли,

16
Кстати, Google изменил свои глупые правила, так что теперь, наконец, они разрешают авто
NoSenseEtAl

Ответы:


192

Между Boost.Thread и стандартной библиотекой потоков C ++ 11 есть несколько отличий:

  • Boost поддерживает отмену потоков, потоки C ++ 11 не
  • C ++ 11 поддерживает std::async, но Boost нет
  • У Boost есть boost::shared_mutexблокировка для нескольких читателей / писателей. Аналогично std::shared_timed_mutexдоступен только с C ++ 14 ( N3891 ), в то время std::shared_mutexдоступен только с C ++ 17 ( N4508 ).
  • Тайм-ауты C ++ 11 отличаются от тайм-аутов Boost (хотя это должно скоро измениться, Boost.Chrono был принят).
  • Некоторые имена разные (например, boost::unique_futureпротив std::future)
  • Семантика передачи аргументов std::threadотличается от использования boost::thread--- Boost boost::bind, которое требует копируемых аргументов. std::threadпозволяет только типы перемещения, такие как std::unique_ptrпередаваемые в качестве аргументов. Благодаря использованию boost::bindсемантика заполнителей, таких как _1вложенные выражения связывания, также может быть различной.
  • Если вы не вызываете явно join()или detach()тогда boost::threadдеструктор и оператор присваивания вызовут detach()объект потока, который уничтожается / назначается. С std::threadобъектом C ++ 11 это приведет к вызову std::terminate()и отмене приложения.

Чтобы прояснить вопрос о параметрах «только для перемещения», ниже приведен действительный код C ++ 11, который передает владение intиз временного std::unique_ptrпараметра параметру, f1когда запускается новый поток. Однако, если вы используете boost::threadего, он не будет работать, так как он используется boost::bindвнутри, и std::unique_ptrне может быть скопирован. Существует также ошибка в библиотеке потоков C ++ 11, предоставляемой с GCC, которая препятствует этой работе, поскольку она также используется std::bindв реализации.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

Если вы используете Boost, то вы, вероятно, можете безболезненно переключаться на потоки C ++ 11, если ваш компилятор поддерживает его (например, в последних версиях GCC в linux в основном реализована полная библиотека потоков C ++ 11, доступная в -std=c++0xрежиме).

Если ваш компилятор не поддерживает потоки C ++ 11, вы можете получить стороннюю реализацию, такую ​​как Just :: Thread , но это все еще является зависимостью.


1
Существуют отдельные методы блокировки / разблокировки для читателей и писателей ( lock/ unlockдля писателей против 'lock_shared / unlock_shared' для читателей). Несколько читателей могут вызывать lock_shared без блокировки, если только писатели его не используют.
Дэйв С

2
В shared_mutexдокументах находятся на boost.org/doc/libs/1_47_0/doc/html/thread/... . Вы можете заблокировать мьютекс как общий или исключительный, а затем использовать соответствующую функцию разблокировки. Вы также можете использовать типы RAII для этого ( shared_lockберет общую блокировку чтения и lock_guardи unique_lockэксклюзивную блокировку). Я попытался прояснить вопрос о типах только для перемещения.
Энтони Уильямс

3
Еще одна незначительная вещь, которая сбила меня с толку : в boost деструктор работающего потока отсоединяет его ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), тогда как в C ++ деструктор запущенного потока вызывает прекращение () (FDIS 30.3.1.3)
Cubbi

3
В C ++ 11 try_scoped_lockфункциональность покрыта std::unique_lock. Существует конструктор, который принимает мьютекс и std::try_to_lock, а затем будет вызывать try_lock()мьютекс, а не lock(). См. Stdthread.co.uk/doc/headers/mutex/unique_lock/…
Энтони Уильямс,

4
Да, Boost.Thread стал намного ближе к стандарту C ++ 11 с тех пор, как я написал это, главным образом благодаря работе Висенте Ботэта.
Энтони Уильямс

24

std::threadв значительной степени смоделирован после boost::thread, с некоторыми отличиями :

  • семантика буста, не копируемая, одноручная, сопоставляет карты с одним потоком, сохраняется. Но этот поток является подвижным, что позволяет возвращать поток из заводских функций и помещать в контейнеры.
  • Это предложение добавляет отмену boost::thread, что является существенным осложнением. Это изменение оказывает большое влияние не только на поток, но и на остальную часть библиотеки потоков C ++. Считается, что это большое изменение оправдано из-за выгоды.
    • Деструктор потока должен теперь вызывать отмену перед отсоединением, чтобы избежать случайной утечки дочерних потоков при отмене родительских потоков.
    • Явный член отсоединения теперь требуется для включения отсоединения без отмены.
  • Понятия дескриптора потока и идентификатора потока были разделены на два класса (это один и тот же класс в boost::thread). Это делается для упрощения манипулирования и хранения идентификаторов потоков.
  • Добавлена ​​возможность создания идентификатора потока, который гарантированно сравнивается с другими присоединяемыми потоками ( boost::threadне имеет этого). Это удобно для кода, который хочет знать, выполняется ли он тем же потоком, что и предыдущий вызов (рекурсивные мьютексы являются конкретным примером).
  • Существует «задняя дверь» для получения дескриптора собственных потоков, чтобы клиенты могли манипулировать потоками, используя базовую ОС, если это необходимо.

Это с 2007 года, поэтому некоторые пункты больше не действительны: теперь boost::threadимеет native_handleфункцию, и, как отмечают комментаторы, std::threadбольше не имеет отмены.

Я не смог найти каких-либо существенных различий между boost::mutexи std::mutex.


2
std::threadне имеет отмены; это boost::threadто, что делает!
Энтони Уильямс

@ Энтони, ты уверен, что не имеешь в виду interrupt()boost :: thread? Также кажется, что это оригинальное предложение, которое изменилось с 2007 года.
Alex B

4
Да, отмена в бусте называется «прерывание». Да, это старое предложение. Последний открытый проект стандарта C ++ 11 (который включает в себя библиотеку потоков) - open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Энтони Уильямс,

6

Есть одна причина не переходить на std::thread.

Если вы используете статическое связывание, std::threadстановится непригодным из-за следующих ошибок / функций gcc:

А именно, если вы позвоните std::thread::detachили std::thread::joinэто приведет к исключению или сбою, то boost::threadв этих случаях работает нормально.


Я вижу, что одна ошибка НЕ ​​ПОДТВЕРЖДЕНА, а другая недействительна, с комментарием о том, что репортер должен был ссылаться на него libpthread.a. Вы абсолютно уверены в том, что говорите?
einpoklum

1
@einpoklum, вы должны быть в состоянии заставить его работать с помощью Wl,--whole-archive -lpthread -Wl,--no-whole-archive, см. этот ответ, например, stackoverflow.com/a/23504509/72178 . Но это не очень простой способ связи, libpthread.aа также считается плохой идеей.
ks1322

4
Можем ли мы предположить, что эти ошибки исправлены, так как это сейчас 2016? Ошибки были опубликованы в 2012 году, и начиная с gcc 4.9.2, он официально поддерживает C ++ 11, поэтому мы не можем пожаловаться на C ++ 11 до официальной поддержки.
Всплеск

6

Корпоративный случай

Если вы пишете программное обеспечение для предприятия, которое должно работать на умеренных или больших операционных системах и, следовательно, собирать с различными компиляторами и версиями компиляторов (особенно относительно старых) для этих операционных систем, я советую вам избегать C ++ 11 в целом на данный момент. Это означает, что вы не можете использовать std::thread, и я бы порекомендовал использовать boost::thread.

Базовый / Tech Startup Case

Если вы пишете для одной или двух операционных систем, вы точно знаете, что вам когда-либо понадобится создавать только с помощью современного компилятора, который в основном поддерживает C ++ 11 (например, VS2015, GCC 5.3, Xcode 7), и вы этого еще не сделали в зависимости от библиотеки наддува, то std::threadможет быть хорошим вариантом.

Мой опыт

Я лично неравнодушен к закаленным, интенсивно используемым, высоко совместимым, высокосогласованным библиотекам, таким как boost по сравнению с очень современной альтернативой. Это особенно верно для сложных предметов программирования, таких как многопоточность. Кроме того, я уже давно добился большого успеха boost::thread(и в целом в Boost) в широком спектре сред, компиляторов, потоковых моделей и т. Д. Когда это мой выбор, я выбираю boost.


1
@UmNyobe Он прав, хотя. Многие реализации потоков C ++ 11 настолько сломаны, что я удивляюсь, что люди даже рассматривают возможность их использования.
StaceyGirl


1

Что касается std :: shared_mutex, добавленного в C ++ 17

Другие ответы здесь дают очень хороший обзор различий в целом. Тем не менее, есть несколько проблем, std::shared_mutexкоторые буст решает.

  1. Восстанавливаемые мутанты. Они отсутствуют в std::thread. Они позволяют преобразовать читателя в писателя, не позволяя другим писателям входить до вас . Они позволяют выполнять такие вещи, как предварительная обработка больших вычислений (например, переиндексация структуры данных) в режиме чтения, а затем обновляться до записи, чтобы применить переиндекс, удерживая блокировку записи только в течение короткого времени.

  2. Честность. Если вы постоянно читаете с std::shared_mutex, ваши писатели будут заблокированы на неопределенный срок. Это потому, что если придет другой читатель, им всегда будет отдан приоритет. При этом boost:shared_mutexвсе потоки в конечном итоге получат приоритет. (1) Ни читатели, ни писатели не будут голодать.

Дело в том, что если у вас система с очень высокой пропускной способностью, без простоев и с высокой конкуренцией, std::shared_mutexвы никогда не будете работать без ручной сборки приоритетной системы поверх нее. boost::shared_mutexбудет работать из коробки, хотя в некоторых случаях вам, возможно, придется повозиться с ним. Я бы сказал, что std::shared_mutexповедение - это скрытая ошибка, ожидающая появления в большинстве кодов, которые ее используют.

(1) Фактический алгоритм использует на основе ОС планировщик потоков. По моему опыту, когда чтение насыщено, в Windows более длинные паузы (при получении блокировки записи), чем в OSX / Linux.


0

Я попытался использовать shared_ptr из std вместо boost и на самом деле обнаружил ошибку в реализации gcc этого класса. Мое приложение зависало из-за того, что деструктор вызывался дважды (этот класс должен быть потокобезопасным и не должен вызывать таких проблем). После перехода на boost :: shared_ptr все проблемы исчезли. Текущие реализации C ++ 11 все еще не являются зрелыми.

Boost также имеет больше возможностей. Например, заголовок в версии std не предоставляет сериализатор потоку (т.е. cout << duration). Boost имеет много библиотек, которые используют свои собственные и т. Д. Эквиваленты, но не взаимодействуют с версиями std.

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


4
shared_ptrДеструктор не должны быть поточно-, это неопределенное поведение , чтобы иметь одну нить с доступом объект , а другой поток разрушает его. Если вы думаете, что нашли ошибку в shared_ptr GCC, пожалуйста, сообщите об этом , в противном случае на балансе вероятности вы используете ее неправильно.
Джонатан Уэйкли
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.