Asynchronouos Socket Communication и фрагментация кучи
Я написал многопоточное приложение Socket Server, которое принимает более 1000 одновременных подключений. Недавно у нас был сбой приложения; после анализа файлов дампа стало известно, что приложение имеет сбой из-за повреждения кучи. Я нашел ту же самую проблему, обсужденную в следующих ссылках.
.NET не имеет надежной асинхронной сокетной связи? http://support.microsoft.com/kb/947862
А также обсуждение предлагает 3 решения.
Сетевое приложение должно иметь верхнюю границу числа ожидающих асинхронных операций ввода-вывода, которые оно публикует.
Используйте Microsoft CCR
Используйте TPL
Из-за временного фактора я думал придерживаться #1, но у меня нет четкой картины, как это реализовать. Кто-нибудь может дать хорошую отправную точку, пожалуйста?
А также кто-нибудь использовал Async с TPL для решения этой проблемы?
2 ответа
Вы имеете в виду лучшую отправную точку, чем публикация в блоге, на которую я ссылался в ответе, на который вы ссылаетесь?
Проблема заключается в следующем:
- Память и другие ресурсы для каждой операции, которые используются во время асинхронной записи, часто "используются", пока стек TCP удаленного однорангового узла не подтвердит данные, и локальный стек не сможет завершить операцию асинхронной записи, чтобы сообщить вам, что вы можете повторно использовать свой буфер.
- Локальный одноранговый узел не имеет никакого контроля над этим, поскольку все это зависит от скорости, с которой удаленный одноранговый узел считывает данные из своего сокета, и от перегруженности канала связи между двумя одноранговыми узлами.
Из-за вышеизложенного вам необходимо жестко ограничить количество асинхронных записей, которые у вас есть в любой момент времени. Вы можете отслеживать это, увеличивая счетчик непосредственно перед тем, как выполнить асинхронную запись, и уменьшив его в обработчике завершения.
Что вы делаете, когда достигнете этого предела, зависит от вас. В оригинальной статье я предпочитаю очередь, в которую помещаются данные для записи. Эта очередь может затем использоваться в качестве источника данных при завершении записи. После того, как очередь пуста, вы можете отправить ее снова в обычном режиме. Конечно, это просто устраняет проблему - у вас все еще есть ресурс памяти, которым управляет удаленный узел (данные в очереди), но у вас также не используются другие ресурсы ОС (пул невыгружаемого пула, ограничение блокировки страниц ввода-вывода, так далее).
Вы можете просто остановить отправку одноранговым сервером, когда достигнете своего предела - и теперь API, который вы строите на основе асинхронного API, должен иметь "не может быть отправлено в данный момент, попробуйте еще раз позже", возврат из отправки, который раньше всегда "Работа".
Если вы делаете это, я бы также серьезно посмотрел на то, как избежать проблемы с закрепленной памятью, выделив большой блок буферов в одном смежном блоке и используя их из пула.
Во-первых, это очень старая статья в КБ. Как вы уверены, что у вас есть именно эта проблема? Затем, как Ханс Пассант отвечает на вопрос SO, если вы напишите неверный асинхронный код, он вас укусит. Если вы не позаботитесь о своих ресурсах (а буферы памяти являются ресурсами), параллельная программа столкнется с ошибками памяти
Очень сложно написать хороший параллельный код, используя необработанные потоки, и TPL делает это проще, но это не исправит ошибки, которые у вас уже есть. На самом деле, если вы не определите свои текущие проблемы, вы, скорее всего, перенесете их в версию, которая использует TPL.
Не зная конкретной проблемы, которая вызвала сбой вашего приложения, я могу только сделать несколько предложений:
- Используйте BufferManager для повторного использования буферов памяти вместо выделения новых.
- Используйте очередь для хранения запросов и их асинхронной обработки вместо того, чтобы начинать новый поток для каждого запроса.
Есть и другие методы, которые вы можете использовать, в зависимости от типа приложения, которое вы создаете. Например, вы можете использовать TPL DataFlow для прерывания обработки в независимых шагах.
Что касается CCR, то нет особого смысла использовать его вне Robotics Studio. TPL содержит большую часть соответствующих функций, необходимых для написания параллельных приложений.