Я реализую клиентское приложение, которое должно выполнить небольшое количество сокетных подключений к аппаратным устройствам. Я разбил проблему на следующее небольшое подмножество кода
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 или другим способом), и если да, то как вы решили ее? Или этот класс просто не безопасен для многопоточного использования?
Это довольно далеко, но стоит попробовать:
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, но если ваш компилятор не реализует поточно-ориентированная инициализация статики области действия, это может сломаться. Вы можете легко проверить это, отследив вызовы этой функции и посмотрев, наблюдаете ли вы потенциальную гонку.
В нескольких примерах они вызывают 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 () где-то вызывается?
Вы всегда должны быть уверены, что iterator != boost::asio::ip::tcp::resolver::iterator()
,
Из форсированных документов:
Созданный по умолчанию итератор представляет конец списка.
Бьюсь об заклад, это проблема, connect()
просто ломает стек из-за неверного конечного итератора.