boost::system::error_code::message() выбрасывает исключение нарушения доступа с boost::asio сокетом
Я реализую клиентское приложение, которое должно выполнить небольшое количество сокетных подключений к аппаратным устройствам. Я разбил проблему на следующее небольшое подмножество кода
boost::system::error_code ec;
std::string str_message = ec.message(); // no access violation before connect()
std::string str_port = "502";
std::string str_ip = "192.168.12.198";
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(),str_ip,str_port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ip::tcp::socket s(io_service);
ec = s.connect(*iterator,ec);
if (ec)
{
// connection error of some kind.
std::string str_debug = ec.message(); // BANG!!!!
}
Я использую Embarcadero RAD studio XE4 C++ Builder, и когда я запускаю приведенный выше код в основном потоке VCL, он работает нормально. Когда я запускаю его с несколькими подключениями, приведенный выше код выполняется в нескольких экземплярах TThread
класс, и именно тогда у меня возникают проблемы с нарушением прав доступа - кажется, что когда error_code
модифицируется connect
вызов, внутренний член m_cat
из error_code
экземпляр становится NULL и поэтому, когда я звоню message()
Я получаю нарушение доступа. Это происходит, даже когда у меня работает только один фоновый поток.
Возможно ли, что мой код выше не является потокобезопасным, так как мне нужно его использовать? Я попытался выяснить, почему этот код не работает в фоновом потоке, но не могу ничего найти о нем.
Буст-версия, которую я использую, составляет 1,50, поскольку это интегрированная версия, которая используется для создания 64-битных приложений в RAD studio.
Кто-нибудь еще сталкивался с этой проблемой в многопоточном режиме (в Embarcadero или другим способом), и если да, то как вы решили ее? Или этот класс просто небезопасен для многопоточного использования?
3 ответа
Это довольно далеко, но стоит попробовать:
system::error_code
состоит из двух записей: значение ошибки и категория. Значение ошибки в основном просто int
, но категория Синглтон. Это необходимо, поскольку категории ошибок сравниваются на равенство на основе идентификаторов указателей (т. Е. Две категории равны тогда и только тогда, когда они указывают на один и тот же объект категории).
Проблема в том, что инициализация категории Singleton может быть не поточно-ориентированной. Асио использует system_category
, который реализован в boost/libs/system/src/error_code.cpp
, Для 1.50 реализация выглядит так:
BOOST_SYSTEM_DECL const error_category & system_category() BOOST_SYSTEM_NOEXCEPT
{
static const system_error_category system_category_const;
return system_category_const;
}
Это гарантированно является поточно-ориентированным на компиляторах, соответствующих C++11, но если ваш компилятор не реализует поточно-ориентированную инициализацию статики области функций, это может привести к поломке. Вы можете легко проверить это, отследив вызовы этой функции и посмотрев, наблюдаете ли вы потенциальную гонку.
Вы всегда должны быть уверены, что iterator != boost::asio::ip::tcp::resolver::iterator()
,
Из форсированных документов:
Созданный по умолчанию итератор представляет конец списка.
Бьюсь об заклад, это проблема, connect()
просто ломает стек из-за неправильного итератора конечной точки.
В нескольких примерах они вызывают io_service->run() в потоке: http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp11/futures/daytime_client.cpp
Мне понравился этот, с ThreadPool: Пул потоков с использованием boost asio
Вы уверены, что io_service->run() где-то вызывается?