Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
#1

Уменьшить нагрузку потока - C++

09.06.2012, 23:14. Просмотров 980. Ответов 15
Метки нет (Все метки)

Делаю многопоточный сервер.
Клиенты подключаются, парсится команда и дальше она отдаётся в очередь.

Создаётся несокль потоков. Все висят на семафоре. Как только добавилась команда, добаляется симафор, и какой то из потоков забирает из очереди класс клиента.
В нём вызывается функция Update.
Так как это ФТП сервер, и может передаваться файл, я сделал отправку и приём частами, на данный момент по 1024 байта.
То есть отдаю 1024 байта или принимаю, и если операция не закончена, то функция Update возвращает false и поток опять становится в очередь. Семафор опять увеличивается на 1.
Дальше опять какой то поток берёт клиента и опять всё по кругу.

Так вот, когда идёт приём или передача файла, получается что приложение начинает тратить 99ю9 процента процессора.
И если в этот момент пытаться вторым соеденением пользоваться - всё очень сильно тормозит...

Вот функция которая крутится в отдельном потоке.
C++
1
2
3
4
5
6
7
8
9
10
11
void FTPThread::Update()
{
    sem_wait(&FTP->sem);
    //Log->Print(LOG_DEBUG, "FTPThread::Update()");
    
    int sock = FTP->getNextQueue();
    FTPSession * ses = FTP->getSession(sock);
    
    if (ses != NULL && ses->Update())
        FTP->addQueue(sock);
}
Вот добавление и удаление в очередь клиентов
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void FTPManager::addQueue(int sock)
{
    //Log->Print(LOG_DEBUG, "FTPManager::addDataQueue");
    queue.push(sock);
    sem_post(&FTP->sem);
}
 
int FTPManager::getNextQueue()
{
    //Log->Print(LOG_DEBUG, "FTPManager::getNextData");
    Lock();
    int sock = 0;
    if (!queue.empty())
    {
        sock = queue.front();
        queue.pop();
    }
    UnLock();
    return sock;
}
Тут выполняется команда клиента за 1 раз, если есть. И в конце идёт вызов функции для канала данных.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
bool FTPSession::Update()
{
    if (!commands.empty())
    {
        Command * c = commands.front();
        commands.pop();
        if (Handler * h = FTPProtocol::getCommands(c->command.c_str()))
        {
            if (h->flag == 0 || isAuth())
            {
                (*this.*h->handler)(c);
            }
            else
            {
                if (Reply * reply = FTPProtocol::getReply(FTPProtocol::ACCESS_DENIED))
                {
                    send(reply->reply);
                }
            }
        }
    }
    return !data_session->Update();
}
 
bool DataSession::Update()
{
    //Log->Print(LOG_DEBUG, "DataSession::Update");
    
    if (!commands.empty())
    {
        Command * c = commands.front();
        if (c->command == "LIST")
        {
            if (sendList())
            {
                commands.pop();
            }
        }
        else if (c->command == "STOR")
        {
            if ((isConnected() || fp != NULL) && storFile(c))
            {
                Log->Print(LOG_DEBUG, "DataSession::STOR END");
                commands.pop();
            }
        }
        
        else if (isConnected() && c->command == "RETR")
        {
            if (retrFile(c))
            {
                commands.pop();
            }
        }
    }
 
    return commands.empty();
}
То есть когда клиент подключен, но нет команд не выполненых, тогда всё отлично, а вот когда выполняется команда, всё ужасно виснит.
Как можно исправить ситуацию?)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
09.06.2012, 23:14
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Уменьшить нагрузку потока (C++):

Вызывть метод одного потока из другого потока - C++
Здравствуйте, подскажите пожалуйста, как можно реализовать такую штуку : есть один поток(1), который вызывает метод у обьекта, этот...

Как определить нагрузку на процессор функцией - C++
Здравствуйте! Подскажите, пожалуйста, как и/или возможно ли написать функцию, которая могла бы определять какую нагрузку на процессор этот...

Создание потока из потока. - C++
Надо создать поток 1, в нем создать поток 2, сам я жуткий дураг, немаловероятно что правильного в коде вообще ничего нет ) ругается...

Два потока, две очереди, два потока - C++
Есть две очереди. Каждая из них заполняется своим потоком. Есть два потока, которые достают данные из своей очереди. Но когда одна из...

Уменьшить последовательность - C++
Дана последовательность из N элементов. Нужно уменьшить её, удалив первый из отрицательных элементов и минимальный из положительных...

Уменьшить число в 2 раза - C++
Дано натуральное число N. Уменьшить число в 2 раза (деление нацело). Проверить, изменилось ли после уменьшения количество разрядов в ...

15
Ksan
27 / 27 / 0
Регистрация: 02.11.2010
Сообщений: 370
09.06.2012, 23:16 #2
можно раз в цикл Sleep(1); но это замедлит выполнение. а можно раз в несколько циклов. тогда ему будет легче
1
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
09.06.2012, 23:30  [ТС] #3
поправка, виснит именно на функции передачи файла на сервер При скачки нагрузка 2-3 процента.

То есть там пока идёт передача, выполнятеся в функции
C++
1
2
3
4
if (isConnected())
    {
        return false;
    }
а другой поток, где сами сокеты опрашиваются, пишет в файл
C++
1
2
3
4
5
6
7
void DataSession::appendData(char* b, int len)
{
    //Log->Print(LOG_DEBUG, "DataSession::appendData");
    fwrite(b, 1,len, fp);
    file_hash.update(b, len);
    file_size += len;
}
Получается что когда весь файл будет передан, произойдёт дисконнект, и тогда поток этот выполнит действия (закроет файл, сохранит данный в бд) и завершится. А поки идёт приём файла, он просто грузит систему...

Добавлено через 1 минуту
Цитата Сообщение от Ksan Посмотреть сообщение
можно раз в цикл Sleep(1);
Ну циклов всего 4, если каждый раз по секунде спать, то при 40 подключенных клиентах по фтп, по 10 клиентов на поток, клиент будет обрабатывать раз в 10 секунд
0
Bers
Заблокирован
09.06.2012, 23:37 #4
По Миллисекунде. (на самом деле может быть и подольше).
Sleep кушает миллисекунды. 1000 миллисекунд - 1 секунда.
4 миллисекунды - вечность для процессора, и слишком мало, что бы ты успел моргнуть.
1
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
09.06.2012, 23:42  [ТС] #5
Я не уточнил, это под линукс, там spleep по секунде, я проверял. Сделал через нанослипп, помогло
Но видимо нужно пересмотреть структуру приложения, так как получается что мы один поток опросту гоняем, пока он мог что то обрабатывать
0
Bers
Заблокирован
10.06.2012, 00:29 #6
Цитата Сообщение от DrobyshevAlex Посмотреть сообщение
Я не уточнил, это под линукс, там spleep по секунде, я проверял. Сделал через нанослипп, помогло
Но видимо нужно пересмотреть структуру приложения, так как получается что мы один поток опросту гоняем, пока он мог что то обрабатывать
Мы на работе используем кросс-платформенное решение. Не могу светить код.
Но думаю, если опишу одну лишь идею, то беды не будет:


Смысл такой: препроцессор определяет где мы хотим взлететь:

1. Линукс: см в сторону gettimeofday()

2. Виндовс: см в сторону QueryPerformanceCounter(), QueryPerformanceFrequency()
(Так же, важно учитывать количество ядрышек, кои участвуют в процессе, ибо под виндовс на много-ядерных могут быть перекосы. Лучше настроить функции на работу с одним ядрышком, а потом восстановить штатную работу)

Таким образом мы засекаем время.

Далее, немножко поспим (ну или множко, это уж как нам захочется):

1. Линукс: usleep(микросенунды) поспать немножко. 10 микросекунд хватит за глаза что бы разгрузить ЦП

2. Виндовс: Если пауза меньше 1000, спим одну милисекунду, иначе - спим val милисекунд/1000

Итого: получаем две функции. Первая засекает время с точностью до микросекунд. Вторая организует задержку в микросекундах.

Профит: единообразная работа под линукс и виндовс. Временные задержки измеряемые в микросекундах. 1 секунда = 1 000 000 микросекунд.
0
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
10.06.2012, 00:34  [ТС] #7
Ну у меня код на epoll, он не то что под виндовс, он под freebds не заработает
Так что думаю пока остановлюсь на одной функции, nanosleep.
Но всё равно хотелось бы избавиться от слипов вообще.

Я вот думаю как то разбить действие на два. То есть когда приходит команда от клиента на передачу файла, я создаю файл и сразу завершаю процесс. А потом отлавливать событие и при отключении сокета добавлять свою команду, что бы опять поток подхватил мой класс, и выпонил дейтвия по сохранению файл а в бд.

Вот только в архитектуру пока это не вписывается) Либо нужно переделать что то либо костылями прукручивать
0
Bers
Заблокирован
10.06.2012, 00:42 #8
ну хз, тут нужно втыкать в ситуацию)) Но пахнет "событийной моделью".
То бишь, циклы дочек не крутятся, пока нечто их не запустит. Нечто должно пырять очень быстро, переодически толкая то, или иное. А все остальное время спать.
Совсем от слипов все равно не получится избавиться.

В винде есть ещё вариант - выставлять кванты времени процессу. Тобишь, можно выставить такой режим, что он будет шуршать на низком приоритете, в фоновом режиме, и не мешать всем остальным. Тогда у него можно крутить циклы без всяких слипов. Он все равно не будет нагружать процессор. Наверняка, нечто подобное присутствует в апи других осей.
0
novi4ok
551 / 504 / 8
Регистрация: 23.07.2009
Сообщений: 2,359
Записей в блоге: 1
10.06.2012, 00:50 #9
а никакого трассирования не предусмотрено? такие вещи без него вообще сложно отлаживать. встрой трассирование (отключаемое), и сходу увидишь, где твоя штучка циклит понапрасну.
0
Avazart
Эксперт С++
7570 / 5555 / 326
Регистрация: 10.12.2010
Сообщений: 24,914
Записей в блоге: 17
10.06.2012, 01:27 #10
Уменьшить нагрузку потока
- понизить приоритет потока
0
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
10.06.2012, 02:39  [ТС] #11
Цитата Сообщение от novi4ok Посмотреть сообщение
а никакого трассирования не предусмотрено? такие вещи без него вообще сложно отлаживать. встрой трассирование (отключаемое), и сходу увидишь, где твоя штучка циклит понапрасну.
Я и так нешел где цикл. Просто он не совсем по напрасну, с мента прихода команды на приём файла и до момента пока сокет примит весь файл - крутится цикл пустой.
Вот я и думаю как от этого избавится. Просто в конце приёма не чего клиент не присылает а просто отключается. У меня в цикле и идёт проверка пока не отключится.


Цитата Сообщение от Avazart Посмотреть сообщение
- понизить приоритет потока
Я просто на пхп пишу обычно, и мало знаком с с++ Погуглю на эту тему
0
novi4ok
551 / 504 / 8
Регистрация: 23.07.2009
Сообщений: 2,359
Записей в блоге: 1
10.06.2012, 03:11 #12
Цитата Сообщение от DrobyshevAlex Посмотреть сообщение
Я и так нешел где цикл. Просто он не совсем по напрасну, с мента прихода команды на приём файла и до момента пока сокет примит весь файл - крутится цикл пустой.
Вот я и думаю как от этого избавится. Просто в конце приёма не чего клиент не присылает а просто отключается. У меня в цикле и идёт проверка пока не отключится.
архитектурка дерьмовенькая. такие вещи ловятся по событию, а не поллингом.
0
DrobyshevAlex
1171 / 1121 / 16
Регистрация: 31.05.2012
Сообщений: 3,059
10.06.2012, 04:13  [ТС] #13
Так вот именно что всё по событиям и ловится. И для всех команд подходит. И сюда бы подошло возможно. Но из за того, что чтение сокетов идёт в одном потоке, а всё остальное в другом, получается что когда чтение с сокета идёт - потоку обрабатывать нечего. Я и говорю что придётся переделывать работу приложения...
0
Ksan
27 / 27 / 0
Регистрация: 02.11.2010
Сообщений: 370
10.06.2012, 11:33 #14
Bers, как спать микросекунду? Виндосовские функции Sleep(), SDL_Delay и тп только в милли принимают
0
Bers
Заблокирован
10.06.2012, 13:58 #15
Цитата Сообщение от Ksan Посмотреть сообщение
Bers, как спать микросекунду? Виндосовские функции Sleep(), SDL_Delay и тп только в милли принимают
Дергаешь не Sleep(милисек), а некий Delay(микросекунды), который внутри себя сам разберётся, под линукс ты, или под виндой. Если под виндой сделает: Sleep(микросекунды/1000), для любого аргумента микросек меньше 1000.
0
10.06.2012, 13:58
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
10.06.2012, 13:58
Привет! Вот еще темы с ответами:

Уменьшить размерность int - C++
Как уменьшить размерность int чтобы вводить только 2х значные числа? (для экономии времени и памяти) И как вычислить время, необходимое...

Все элементы массива уменьшить на 20 - C++
Дан массив. Все его элементы: а) уменьшить на 20. б) умножить на последний элемент. в) увеличить на число В. пожалуйста помоги!!!

Числа из строки уменьшить в 10 раз - C++
Дана текстовая строка, содержащая слова и целые числа, разделенные пробелом. Необходимо все цифры уменьшить в 10 раз. Сказали что задание...

Введенное отрицательное число уменьшить на 2 - C++
Если введено отрицательное число требуется отнять от него 2. Так не получается ввожу -5 оно выводит -5... #include <iostream> ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru