Название: Оптимизация многопоточного сервера Отправлено: merke от Март 25, 2010, 11:18 Всем привет!
В общем накидал многопоточный сервер на Qt, реализовал всё без использования QThread. В потоках я не очень, поэтому хочу посоветоваться, какие функции раскидать по потокам. Очень нужна Ваша помощь. ВОт привожу весь код сервера: MainWindow.h Код: #ifndef MAINWINDOW_H MainWindow.cpp Код: #include "mainwindow.h" Название: Re: Оптимизация многопоточного сервера Отправлено: SABROG от Март 26, 2010, 23:23 Цитировать В общем накидал многопоточный сервер на Qt Асинхронный - да. Многопоточный - нет. Главный недостаток такой реализации в том, что любой метод, который выполняется дольше всех - блокирует основной цикл событий и как следствие все клиенты вынуждены ждать ответа от сервера до тех пор пока управление не вернется в этот цикл событий. Пример: сервер пытается передать файл клиенту. В этот момент срабатывает Касперский и просит пользователя принять решение, что делать с этим файлом. Если пользователя в этот момент нет за компьютером, то диалог Касперского будет висеть вечно, а операционная система не возвратит управление с ошибкой в приложение, которое пытается открыть файл до того момента пока антивирус этого не разрешит. В случае с многопоточной системой несколько циклов событий в каждом потоке могли бы запускать таймер по истечении которого грохать нерадивый поток, застравший на чтении файла, а клиентов просить переподключитсья, чтобы поймать их в новом потоке. Файл можно пометить как "плохой" и не предпринимать попытки чтения до того как придет хозяин и не разберется с этим.Эти инклуды замедляют процесс компиляции: Код
На эти классы QTcpServer, QTcpSocket, QBuffer в заголовках используются только указатели, на которые известны размеры компилятору. Поэтому достаточно Forward Declaration: Код
Но в .cpp файле нужно будет сделать включение их инклудов, а инклуд QMessageBox перенести вообще в .cpp файл. Часто получается так, что используется один заголовок на несколько .cpp файлов, причем некоторым из них не нужны инклуды классов, которые они используют. Код
foreach делает неявную копию всех элементов списка. В случае указателей наверно это небольшая потеря производительности, но она нам нужна? Код Возможно компилятор это и сможет оптимизировать, но может лучше isEmpty()? Код Может лучше QIntValidator? Он позволяет задать и минимум, и максимум, и отсеять все, что не целочисленное. Да и от этих дополнительных проверок избавится: Код
А вообще, может лучше обычный QSpinBox поставить, чтобы не приходилось столько проверок делать?: Код
Вместо пустоты там вполне может находится минимальное значение для порта и проверка не понадобится. Может быть сделать смену состояний через QStateMachine и вынести в конструктор? Будет легче поддерживать приложение, если добавятся или пропадут кнопки/виджеты. Код: void MainWindow::slotStartClicked() Аналогично вслед за сменой состояния можно динамически менять текст на QLabel: Код
Item based widgets довольно медленны на больших объемах элементов. Лучше уж QPlainTextEdit, там хоть можно выставить setMaximumBlockCount() и сервер не рухнет, если ты его решишь на недельку другую оставить без присмотра. Код
Не нужно распихивать tr() для динамических строк. Я не думаю, что ты потом захочешь перевести английский текст типа "%1" на русский, например "Процент 1", а когда речь идет о цифре (peerPort()), то я думаю американцев в школе тоже учат арабским цифрам. Или ты хочешь обозначать цифры буквами как в Древней Руси? Код
Код
Вместо хранения обычных указателей в списках можно хранить QSharedPointer, при удалении любого из таких элементов из списка память освободится сама и в этом коде потребность отпадет: Код
Базовый класс в QTcpSocket это QObject, думаю тут правильней будет использовать qobject_cast вместо static_cast: Код
Следующий код по меньшей мере странный: Код
Когда приходят данные ты добавляешь их в буффер и выводишь в textEdit. Запросто может произойти ситуация, когда данные приходят частями, тогда твой textEdit будет выглядеть так: Код: He Это уже касается самого протокола. Обычно люди шлют размер данных, который собираются передавать и лишь когда буффер станет равный этому размеру выводят результат. Стоит учесть, что в Unicode один символ - 2 байта, поэтому QString::length() или size() вернут значение в 2 раза меньше. Попытка записи в закрытый сокет? Или "извини пользователь, я не могу тебе дать отключиться, твой компьютер мой раб навеки!"? Код
Цитировать В потоках я не очень, поэтому хочу посоветоваться, какие функции раскидать по потокам. Очень нужна Ваша помощь. Возвращаясь к главному вопросу. Потоков сделай изначально 5 (вообще нужно писать систему, которая бы сама вычисляла производительность сервера в зависимости от количества доступной памяти и потоков, но для начала можно поставить и фиксированное значение). Клиентов раскидывать равномерно по потокам. Соединение/разъединение всех клиентов можно осуществлять в одном потоке, в то время как обработку данных в остальных пяти. Наша задача вынести самые долги функции в отдельные потоки. Как правило это функции подготовки данных на запись в поток. Чтение с винчестера, долгие математические вычесления и тому подобное. В общем всё то, что может заморозить основной цикл событий и как следствие привести к отказу обслуживания сервером вместо того, чтобы "кормить" клиента обещаниями "вот вот, уже скоро, еще 5 минут и я тебе пришлю твои фотки". Исходя из этого у тебя сейчас только один, потенциальный, участок, который в будущем может стать "узким горлышком": Код
Тут у тебя данные читаются из сокета и затем отправляются обратно. То есть получился echo server. На его месте могла бы быть пересылка тяжелого файла, поэтому в строках предшествующих buffer->readAll() должен происходить эмит сигнала о необходимости подготовки данных, который отправляется другому потоку. Соответственно строчка sendToClients() должна будет перенесена в слот, который будет ответом на сигнал из подготавливающего потока о том, что данные готовы к пересылке. Кстати убери весь код, относящийся к работе с GUI из слотов обрабатывающих сокеты. Все общение с GUI нужно реализовать посредством сигналов, а не прямых вызовов слотов виджетов. Только в этом случае мы можем гарантировать то, что никакое взаимодействие с виджетами не тормозит работу сервера, так как сигналы будут вставать в очередь и гарантировать нам то, что их обработка основым, GUI'шным потоком не блокируют работу, то есть не будет необходимости ждать завершения работы слотов типа ui->textEdit->append(line); Нам ведь не нужен возвращаемый результат? Тогда нафига ждать? Название: Re: Оптимизация многопоточного сервера Отправлено: zeonET от Апрель 08, 2010, 17:29 спасибо, очень много нового узнал, занесу в закладки и буду перечитывать ))
Название: Re: Оптимизация многопоточного сервера Отправлено: niXman от Апрель 08, 2010, 21:04 SABROG, merke, работа идет? или уже забили?
Название: Re: Оптимизация многопоточного сервера Отправлено: SABROG от Апрель 09, 2010, 08:22 SABROG, merke, работа идет? или уже забили? На данном этапе эта тема выходит за рамки моих интересов. |