"2" поставил ручками, руководствуясь
[msdn]Maximum number of threads that the operating system can allow to concurrently process I/O completion packets for the I/O completion port
[/msdn]
т.к. хочу таки протестить на двух потоках, а проц в машине один
int Run()
{
......
......
BOOL bIORet = ::GetQueuedCompletionStatus(hCompletionPort, &unIoSize, (LPDWORD)&pSocket, &pOverlapped, 5000);
if (!bIORet)
{
}
else
{
//обработка данных
}
....
.....
return 0;
}
Проблема. При одном потоке все работает прекрасно, при двух я ооочень часто не получаю READ, т.е. как будто до меня не доходит пакет. GetQueuedCompletionStatus просто провисает по таймауту во всех потоках. Но! Если поставить брейк-поинт на хотя бы "if (!bIORet)", то новые пакеты я получать начну (старые уже никогда ). Посылка при любом количестве потоков успешна. Как быть?
1. Сокет у Вас создается с флагом WSA_FLAG_OVERLAPPED? ( если Вы используете вызов socket, то флаг установлен )
2. Данные читаются с помощью WSARecv, куда передается 6ым параметром пустая структура OVERLAPPED и эта структура остается валидной до окончания операции? Если эта структура используется многократно, ее нужно обновлять ( обнулять ) перед каждым использованием.
3. Вы учитываете, что операция чтения может быть выполнена синхронно?
4. Мне честно говоря немного смущает вот это:
(LPDWORD)&pSocket, &pOverlapped
Если p — это префикс указателя, то разыменование указателя в данном случае IMHO не уместно?
Здравствуйте, TarasCo, Вы писали:
TC>1. Сокет у Вас создается с флагом WSA_FLAG_OVERLAPPED? ( если Вы используете вызов socket, то флаг установлен )
Именно так.
TC>2. Данные читаются с помощью WSARecv, куда передается 6ым параметром пустая структура OVERLAPPED и эта структура остается валидной до окончания операции? Если эта структура используется многократно, ее нужно обновлять ( обнулять ) перед каждым использованием.
Обнуления действительно не было. Добавил, проверю.
TC>3. Вы учитываете, что операция чтения может быть выполнена синхронно?
Поясните, пожалуйста.
TC>4. Мне честно говоря немного смущает вот это: TC>(LPDWORD)&pSocket, &pOverlapped TC>Если p — это префикс указателя, то разыменование указателя в данном случае IMHO не уместно?
С разыменованием думаю проблем нет. Указатель на объект "Сокет" отдаю так
BOOL bSuccess = ::PostQueuedCompletionStatus(hCompletionPort, 0, (DWORD)pSocket, &pOverlapBuff->m_ol);
//pOverlapBuff поинтер на объект такого вида;
//class OverlapBuff
//{
//public:
//.......
//.......
// OVERLAPPED m_ol;
// WSABUF m_wsabuf;
//.......
//.......
//};
Здравствуйте, Michael Chelnokov, Вы писали:
MC>Здравствуйте, mLapo, Вы писали:
L>>BOOL bSuccess = ::PostQueuedCompletionStatus(hCompletionPort, 0, (DWORD)pSocket, &pOverlapBuff->m_ol);
MC>Кстати, а что этим делается?
после этого жду нотификации, что Send прошел в IOCP потоке.
Понятно, что буферы на отправку/получение обложены критическими секциями, но локировки в этих местах нет.
TC>>3. Вы учитываете, что операция чтения может быть выполнена синхронно? MC>В этом случае все равно уведомление будет.
Это понятно, но может быть какая то логическая ошибка, типа
status = WSARecv
if ( status == STATUS_PENDING )
{
GetQueueCompletionStatus
}
У меня стойкое ощущение, что Вы не понимаете назначение функции или используете ее не самым очевидным способом PostQueuedCompletionStatus. Советую почитать маны. В обычном случае, это функция не очень то нужна. Мне на ум приходит такой сценарий использования ( говорим про отправку данных ) IOCP:
void
sending_thread()
{
do {
//получаем уведомление о завершившейся операции записи. Это значит что в этот сокет можно еще накидать данных
GetQueueCompleteionStatus
//кидаем данные
WSASend
} while( 1 );
}
Таких потоков можно создать несколько. И никаких PostQueuedCompletionStatus.
Здравствуйте, TarasCo, Вы писали:
TC>Это понятно, но может быть какая то логическая ошибка, типа
Да, там скорее всего автор запутался в последовательностях происходящих событий/вызовов. С его множеством PostQueuedCompletionStatus это неудивительно.
For mLapo: Вообще-то PostQueuedCompletionStatus обычно используется в том случае, когда надо "завернуть" на IOCP нечто, изначально IOCP не поддерживающее. Например, у тебя уже есть некий черный ящик, который уведомляет о происходящем через какие-нибудь callback'и. Ты не хочешь для него плодить отдельных потоков, а хочешь чтобы уведомления от него обрабатывались в пуле потоков-обработчиков IOCP. Тогда и стОит использовать PostQueuedCompletionStatus. Частный случай такого черного ящика — команды SCM Windows-сервису.
Здравствуйте, Michael Chelnokov, Вы писали: MC>Здравствуйте, TarasCo, Вы писали: TC>>Это понятно, но может быть какая то логическая ошибка, типа MC>Да, там скорее всего автор запутался в последовательностях происходящих событий/вызовов. С его множеством PostQueuedCompletionStatus это неудивительно. MC>For mLapo: Вообще-то PostQueuedCompletionStatus обычно используется в том случае, когда надо "завернуть" на IOCP нечто, изначально IOCP не поддерживающее. Например, у тебя уже есть некий черный ящик, который уведомляет о происходящем через какие-нибудь callback'и. Ты не хочешь для него плодить отдельных потоков, а хочешь чтобы уведомления от него обрабатывались в пуле потоков-обработчиков IOCP. Тогда и стОит использовать PostQueuedCompletionStatus. Частный случай такого черного ящика — команды SCM Windows-сервису.
Во-первых, спасибо вам за конструктивный диалог. C IOCP не работал раньше, может где-то и туплю.
Переписал код, сделав ненужными PostQueuedCompletionStatus.
Вначале была такая схема.
Мне нужно отправить данные:
1) В рабочем потоке создаю OverlapBuff с пометкой "IWrite".
2) В рабочем потоке вызываю PostQueuedCompletionStatus передав туда сокет, который хочет отправить и pOverlapBuff
3) В потоке IOCP отлавливаю IWrite, достаю из прараметров GetQueuedCompletionStatus сокет, после чего вызываю WSASend с необходимыми параметрами среди которых и "новый" рOverlapBuff, но уже с пометкой "IWriteComplete".
4) по получению IWriteComplete некоторым образом обновляю буфер сокета.
С получением в принципе все тоже самое, только после получения данных я снова иницирую чтение вызвав WSARead
Переписал.
Теперь при желании отправить данные, буду сразу слать и ждать нотификации об успешной отправке. С получение тоже самое. Без PostQueuedCompletionStatus. Буду тестировать.
Здравствуйте, Michael Chelnokov, Вы писали:
MC>Здравствуйте, mLapo, Вы писали:
L>>А к чему вопрос?
MC>Подозреваю что к тому что, судя по твоему описанию, тебе наплевать на возможное перемешивание отправляемых данных.
Нет, данные не перемешиваются. Пока не получено подтверждения об отправке (WriteComplete), новые данные лишь добавляются к буферу. После получения подтверждения, я проверяю буфер на наличие данных, если оные существуют инициирую следующую отправку.
На перемешивание и многократную посылку одних и тех же данных уже наступил
Здравствуйте, mLapo, Вы писали:
L>На перемешивание и многократную посылку одних и тех же данных уже наступил
Offtopic: Честно говоря, я в последние годы солидарен с противниками потоков. В начале всё прекрасно и просто. Потом оказывается что не учел возможности что-то испортить в параллельных потоках. Потом, после того как защитил всё что надо и что не надо, оказывается что существуют дедлоки. После убирания ненужных защит оказывается что данные снова портятся. И так далее по кругу. После нескольких кругов останавливаешься и думаешь — "ну и зачем мне нужны были эти потоки?". И так до следующего проекта
Однако, мы под Windows. Тут это "естественно"...
MC>Offtopic: Честно говоря, я в последние годы солидарен с противниками потоков.
Я самый главный противник потоков . Многопоточный сетевой сервер — это штука для большой нагрузки. Но у нас все разработчики считают, что если соединений больше одного — нужна масштабирующаяся система. И начинается... В половине случаев IMHO можно обойтись моделью — принял соединение, обработал запрос, отдал данные, закрыл соединение, заснул в ожидании следующего. Возросла нагрузка и начались лаги? Купите 4-х процессорный сервер и руками админа запустите 4 экземпляра. Но у нас как и во всем — нужна машина — только 3л полный привод, дом — кирпичный трех-этажный, сервер — круче чем Oracle .