Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.22.68.29] |
|
Страницы: (3) [1] 2 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
К сожалению буст для меня это непонятный темный лес.
Нужен ваш хелп. Сервер у меня многопоточный. Но я правильно понимаю что объект boost::asio::io_service должен быть один единственный в приложении? В каждом потоке достаточно вызвать io_service.run(); правильно? Тут у меня возникает первая затыка. Которую я пока не знаю как решить. Мне необходимо в каждой асинхронной операции будь то чтение или запись знать в каком потоке она вызвалась. у меня имеется обертка над потоком - свой класс workerThread. При старте сервера я создаю ХХХ объектов workerThread и у каждого такая потоковая функция void workerThreads::workingThread( ) { server s( io_service, m_serverport ); io_service.run(); } |
Сообщ.
#2
,
|
|
|
Вызывай GetCurrentThreadId(). Или тебе нужен конкретный указатель на объект workerThread? Тогда можно сделать глобальную мапу ThreadId -> workerThread
|
Сообщ.
#3
,
|
|
|
Или так:
__declspec(thread) workerThreads* WorkerThread = nullptr; ... void workerThreads::workingThread( ) { WorkerThread = this; server s( io_service, m_serverport ); io_service.run(); } |
Сообщ.
#4
,
|
|
|
Цитата Pacific @ Вызывай GetCurrentThreadId(). Или тебе нужен конкретный указатель на объект workerThread? Тогда можно сделать глобальную мапу ThreadId -> workerThread Я к этому "решению" и пришел. workerThread** m_threadsmap = NULL; m_threadsmap = new LPWORKERTHREADPTR[ 0x10000 ]; memset( m_threadsmap, 0, sizeof(LPWORKERTHREADPTR) * 0x10000 ); и заношу в массив указатель на потоковый объект по его id перерасход памяти есть но зато мгновенный доступ в отличие от std::map for (int i = 0; i < numberOfThreads; i++) { workerThread* th = new workerThread( m_dbpool, io_service ); int threadid = th->createThread( getServerPort()); m_threadsmap[ threadid ] = th; } Я сделал "опасное" допущение что ID потока в 32 битной windows не превысит 0хFFFF - лично я ни разу не сталкивался чтобы оно было больше 0x4000 далее в одном и потоков куда меня коллбэк boost.asio выкинул такой вот код: int threadid = GetCurrentThreadId(); workerThread* th = m_threadsmap[ threadid ]; вопрос в том - насколько по корану данный сабж. и не является ли мега кривыми костыялми. может быть у буста есть фича чтобы в коллбэк передать какие то данные о потоке в котором он вызвался. |
Сообщ.
#5
,
|
|
|
progman
Это костыль, да. Лучше переделай на thread local глобальную переменную (пост №3) - доступ также мгновенный, только перерасхода памяти не будет, и не будет зависеть от предположений о threadid. |
Сообщ.
#6
,
|
|
|
Цитата Pacific @ progman Это костыль, да. Лучше переделай на thread local глобальную переменную (пост №3) - доступ также мгновенный, только перерасхода памяти не будет, и не будет зависеть от предположений о threadid. че то я туплю - как мне оно поможет? __declspec(thread) workerThreads* WorkerThread = nullptr; ... void workerThreads::workingThread( ) { WorkerThread = this; server s( io_service, m_serverport ); io_service.run(); } ? |
Сообщ.
#7
,
|
|
|
progman
WorkerThread будет глобальной переменной, но своей для каждого потока. Где надо - получай указатель на свой объект, локальный для текущего потока. |
Сообщ.
#8
,
|
|
|
Решили соскочить с IOCP на буст?
Интересный опыт, поделитесь потом своим мнением - такое сравнение будет очень полезно для всех. |
Сообщ.
#9
,
|
|
|
Цитата Oleg2004 @ Решили соскочить с IOCP на буст? Интересный опыт, поделитесь потом своим мнением - такое сравнение будет очень полезно для всех. под виндой boost::asio через IOCP и работает. Добавлено еще момент, комильфо ли отсылать данные клиенту так: HRESULT connection::send( char* buffer, DWORD length, LPWSAOVERLAPPED lpOverlapped ) { auto self(shared_from_this()); boost::asio::async_write( m_socket, boost::asio::buffer(&length, header_length), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { } }); boost::asio::async_write( m_socket, boost::asio::buffer( buffer, length), [this, self](boost::system::error_code ec, std::size_t /*length*/) { if (!ec) { } }); return S_OK; } сначала шлю 4 байта размер а потом данные. в WSASend можно было два буфера указать и отправить за один вызов. Там оно более эстетично в коде. А как в бусте поэстетичнее? И я надеюсь тут я асинхронную передачу данных делаю :-) |
Сообщ.
#10
,
|
|
|
Цитата progman @ В каждом потоке достаточно вызвать io_service.run(); правильно? Да. Цитата progman @ Мне необходимо в каждой асинхронной операции будь то чтение или запись знать в каком потоке она вызвалась. boost::this_thread::get_id() ? Добавлено Цитата progman @ А как в бусте поэстетичнее? Никогда так не делал. Затрудняюсь сказать, обеспечивает ли буст последовательность исполнения команд из очереди на нескольких потоках. Лучше в обработчике завершения первого write пошли второй. И, скати, почему нельзя отправить сразу 1м пакетом? |
Сообщ.
#11
,
|
|
|
Цитата shm @ Никогда так ты не делал. Затрудняюсь сказать, обеспечивает ли буст последовательность исполнения команд из очереди на нескольких потоках. по идее оно в одном потоке вызывается. сейчас запустил тест - 15 тысяч клиентов третий час нормально пашут. но что то тоже стали сомнения терзать. Хотя с другой стороны мне важно чтобы сначала ушел заголовок пакета - просто 4 байта а потом тело. И не важно из одного потока или из разных. по идее это гарантируется последовательным вызовом... но что там внутри... уже сомнения пошли Цитата shm @ Лучше в обработчике завершения первого write пошли второй. придется выделять память и хранить передаваемый буфер. |
Сообщ.
#12
,
|
|
|
Цитата progman @ boost::asio через IOCP Ну да: #if defined(BOOST_ASIO_HAS_IOCP) #include <boost/scoped_ptr.hpp> #include <boost/asio/io_service.hpp> #include <boost/asio/detail/mutex.hpp> #include <boost/asio/detail/op_queue.hpp> #include <boost/asio/detail/socket_types.hpp> #include <boost/asio/detail/timer_op.hpp> #include <boost/asio/detail/timer_queue_base.hpp> #include <boost/asio/detail/timer_queue_fwd.hpp> #include <boost/asio/detail/timer_queue_set.hpp> #include <boost/asio/detail/win_iocp_io_service_fwd.hpp> #include <boost/asio/detail/win_iocp_operation.hpp> #include <boost/asio/detail/thread.hpp> #include <boost/asio/detail/push_options.hpp> Такое нагромождение всего чего - не, я лично сторонник АПИ. Все библиотеки подобного рода - это инструмент для RAD (rapid application development) Быстро сбацать и запустить. Очень даже возможно, что и будет неплохо работать - для определенных целей. И, как нам всем понятно - внутри классов буста - все тот же виндовский АПИ... Впрочем это все только ИМХО. Вот тут интересные мысли - вроде по теме. И кстати о проблеме - протокол вида "frame_size->frame_body" |
Сообщ.
#13
,
|
|
|
Посмотрел, последовательность в твоем случае будет соблюдена. Обратил еще раз внимание на твой код, вот так:
Цитата progman @ boost::asio::buffer(&length, header_length) делать нельзя. Т. к. должна обеспечиваться гарантия существования объекта до завершения операции. Другими словами, boost::asio::buffer работает по ссылке. Добавлено Цитата progman @ придется выделять память и хранить передаваемый буфер. Ты так говоришь как будто это так сложно организовать. |
Сообщ.
#14
,
|
|
|
Цитата shm @ делать нельзя. Т. к. должна обеспечиваться гарантия существования объекта до завершения операции. Другими словами, boost::asio::buffer работает по ссылке. спасибо. не учел. Думал там копирование внутри. HRESULT connection::send( char* buffer, DWORD length, LPWSAOVERLAPPED lpOverlapped ) { char* sendbuffer = new char[length + header_length]; memcpy( &sendbuffer[ 0 ], &length, header_length); memcpy( &sendbuffer[ header_length ], buffer, length ); boost::asio::async_write( m_socket, boost::asio::buffer( sendbuffer, length + header_length ), std::bind( &connection::handle_write, shared_from_this(), sendbuffer, std::placeholders::_1, std::placeholders::_2 ) ); return S_OK; } void connection::handle_write(char* buffer, const boost::system::error_code& a_error, std::size_t a_size) { delete[] buffer; } |
Сообщ.
#15
,
|
|
|
Я у себя привязывал буфер данных чтения и записи к объекту, связанному с сокетом. Не скажу, что это самый правильный подход, но рабочий.
|