Что именно делает `threading = multi` при компиляции boost?

Я не совсем уверен, что именно делает флаг threading=multi при создании boost. В документации указано:

 Causes the produced binaries to be thread-safe. This requires proper
 support in the source code itself.

который, кажется, не очень специфичен. Означает ли это, что доступ к, например, форсированным контейнерам, обеспечивается mutexes/locks или аналогичным? Поскольку производительность моего кода имеет решающее значение, я хотел бы свести к минимуму любые ненужные мьютексы и т.д.

Дополнительная информация:

Мой код - это подключаемая DLL-библиотека, которая загружается в многопоточное стороннее приложение. Я статически связываю boost в DLL (плагину не разрешено иметь какие-либо другие зависимости, кроме стандартных DLL файлов Windows, поэтому я вынужден это сделать).

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

Я попытался повысить уровень сборки с помощью threading=multi и threading=single, и оба они работают, но мне очень хотелось бы знать, что я здесь делаю.

Ответ 1

Нет, threading=multi не означает, что такие вещи, как форсированные контейнеры, внезапно станут безопасными для одновременного доступа несколькими потоками (что было бы чрезмерно дорогостоящим с точки зрения производительности).

Скорее, теоретически это означает, что boost будет скомпилирован для понимания потоков. В основном это означает, что методы и классы boost будут вести себя разумным образом по умолчанию при доступе к нескольким потокам, подобно классам в std-библиотеке. Это означает, что вы не можете получить доступ к одному и тому же объекту из нескольких потоков, если не оговорено иное, но вы можете безопасно обращаться к различным объектам из нескольких потоков. Это может показаться очевидным даже без явной поддержки, но любое состояние static, используемое библиотекой, нарушит эту гарантию, если не будет защищена. Использование threading=multi гарантирует, что любое такое разделенное состояние является свойством, защищенным мьютеком или каким-либо другим механизмом.

В прошлом аналогичные аргументы или stdlib были доступны для библиотек C и С++ std, поставляемых с моими компиляторами, хотя сегодня в основном доступны только многопоточные версии.

Вероятно, существует небольшой недостаток в компиляции с threading=multi, учитывая, что необходимо синхронизировать только ограниченное количество статического состояния. Ваш комментарий о том, что ваша библиотека будет в основном вызвана только одним потоком, не внушает большой уверенности - в конце концов, это те скрытые ошибки, которые заставят вас проснуться в 3 часа утра вашим боссом после ночи длительного питья.

Пример boost shared_ptr информативен. При threading=single даже не гарантируется, что независимая манипуляция двумя экземплярами shared_ptr из нескольких потоков безопасна. Если они указывают на один и тот же объект (или, теоретически, при некоторых экзотических реализациях, даже если они этого не делают), вы будете генерировать поведение gen undefined, потому что совместное состояние не будет обрабатываться с надлежащей защитой.

С threading=multi этого не произойдет. Тем не менее, он все еще небезопасен для доступа к тому же экземпляру shared_ptr из нескольких потоков. То есть, он не дает никаких гарантий безопасности потоков, которые не документированы для рассматриваемого объекта, но это дает гарантии "ожидаемые/разумные/дефолтные" независимые независимые объекты. Не существует хорошего названия для этого уровня безопасности потоков, который по умолчанию является стандартным, но на самом деле это то, что в целом предлагает все стандартные библиотеки для многопоточных языков сегодня.

В качестве конечной точки стоит отметить, что Boost.Thread неявно всегда компилируется с помощью threading=multi - поскольку использование многопоточных классов boost является неявным намеком на то, что присутствуют несколько потоков. Использование Boost.Thread без многопоточной поддержки было бы бессмысленным.

Теперь, все сказанное выше, теоретическая идея компиляции boost "с поддержкой потоков" или "без поддержки потоков", которая является целью флага threading=. На практике, поскольку этот флаг был введен, многопоточность стала стандартной, и однопоточное исключение. Действительно, многие компиляторы и компоновщики, которые по умолчанию не выполняли однопоточное поведение, теперь по умолчанию используют многопоточность - или, по крайней мере, требуют только одного "подсказки" (например, наличия -pthread в командной строке) для переключения на многопоточность.

Кроме того, были предприняты согласованные усилия, чтобы сделать сборку "умных" - тем, что она должна переходить в многопоточный режим, когда окружающая среда его поддерживает. Это довольно расплывчато, но обязательно так. Это становится настолько сложным, как слабая связь символов pthreads, так что решение использовать MT или ST-код фактически отложено на время выполнения - если pthreads доступно во время выполнения, эти символы будут использоваться, иначе слабосвязанные заглушки - которые ничего не делают на всех - будет использоваться.

Суть в том, что threading=multi является правильным и безопасным для вашего сценария, и особенно если вы создаете двоичный файл, вы будете распространять его на другие хосты. Если вы этого не сделаете, очень вероятно, что он будет работать в любом случае из-за эвристики времени сборки и даже эвристики времени исполнения, но вы используете возможность тихо использовать пустые методы заглушки или иным образом использовать MT-небезопасный код. Существует немного недостатка в использовании правильного варианта, но некоторые детали gory также можно найти в комментариях к этому моменту, и Игорь ответил также.

Ответ 2

После некоторого копания получается, что threading=single не имеет большого эффекта, как и следовало ожидать. В частности, он не влияет на макрос BOOST_HAS_THREADS и, следовательно, не позволяет библиотекам принимать однопоточную среду.

С gcc threading=multi просто подразумевается #define BOOST_HAS_PTHREADS, в то время как с MSVC он не производит никакого видимого эффекта. В частности, _MT определяется как в режимах threading=single, так и threading=multi.

Обратите внимание, однако, что можно явно настроить библиотеки Boost для однопоточного режима, указав соответствующий макрос, например BOOST_SP_DISABLE_THREADS, BOOST_ASIO_DISABLE_THREADS или глобально с BOOST_DISABLE_THREADS.

Ответ 3

Пусть будет кратким. Causes the produced binaries to be thread-safe означает, что код Boost адаптирован к тому, что разные потоки могут использовать разные объекты Boost. Это означает, в частности, что код Boost будет заботиться о безопасности потоков для доступа к скрытым глобальным или статическим объектам, которые могут быть использованы при реализации библиотек Boost. Он не позволяет автоматически использовать разные темы для одновременного использования одних и тех же объектов Boost без защиты (locks/mutexes/...).

Изменить: Некоторые библиотеки Boost могут документировать дополнительную безопасность потоков для определенных функций или классов. Например asio::io_service, как было предложено Игорем Р. в комментарии.

Ответ 4

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

Думаю, включение этой опции - это путь.

Дополнительная информация: Библиотеки BOOST в режиме многопоточности