[Спор] Многопоточное или однопоточное ядро системы
От: meto Россия  
Дата: 24.12.11 08:50
Оценка:
На работе произошёл спор по поводу концепции ядра основного модуля нового проекта. Сам проект долгосрочный и в перспективе довольно таки не маленький выходит (по описанию тех. задания).
Со своей стороны я предложил многопоточную реализацию ядра, а мой коллега предложил однопоточную реализацию ядра. Сейчас к спору уже подключилось несколько коллег (включая начальника отдела), но всё равно не можем однозначно определить лучшее решение (учитывая долгосрочность и расширяемость системы).

Сразу хочу сказать, что описанная далее задача выдумана. Но суть вопроса от описанной задачи не меняется.

Задача.
Дано 10 FTP-серверов и наша клиентская машинка, для которой и требуется написать приложение (Win32 GUI).
Допустим, что связь с FTP-серверами очень плохая (чуть лучше GPRS).
На каждом FTP-сервере есть лог-файл, который постоянно обновляется (мы имеем права на чтение этого файла).
Основные задачи приложения:
1) Получение новой информации из лог-файла с каждого FTP-сервера (скачивается не весь лог-файл, а только добавленная часть).
2) Обработка полученной с FTP-сервера информации (возможно как архивирование полученных данных, так и просто расчёт MD5-хеша).
3) По результатам обработки возможны 2 ситуации:
    a) отправка заархивированных данных на удалённый FTP-сервер;
    б) отправка нотификационного письма на некоторый email.

Примечания:
Результирующие данные архивируются на диск, а в блок отправки результатов передаётся полное имя файла (архивы могут быть под сотни мегабайт).
От приложения не требуется моментальная выдача результатов (приемлемо получение новых данных в течение 8 минут).


Многопоточный вариант реализации (мой концепт):
m) Главный поток — цикл ожидания оконных событий (кнопка Exit, завершение работы ОС и т.п.).
c) Контролирующий поток — обслуживает и следит за потоками 1,2,3 (даёт сигнал завершения работы и т.п.).
1) Скачивающий поток — последовательно коннектится и скачивает новые данные с каждого FTP-сервера.
2) Обрабатывающий поток — по результатам обработки возможна как архивация полученных данных, так и просто расчёт MD5-хеша.
3) Отправляющий поток — отправка заархивированных данных на удалённый FTP-сервер или отправка короткого email.

В 1-ом потоке перед переходом к следующему FTP установлен Sleep(500), а перед переходом к FTP#1 установлен Sleep(5000), чтобы снизить нагрузку на CPU.
Для потоков 2 и 3 существуют свои очереди, в которых скапливаются задания для них.
Если в 1-ом потоке очередь на закачку файла дошла до FTP#7 и при этом во 2-ом потоке всё ещё выполняется обработка данных с этого FTP (присутствует в очереди), то в 1-ом потоке пропускаем FTP#7 и переходим к FTP#8.
Все email помещаются в начало очереди в 3-ем потоке, потому что имеют больший приоритет.
Все операции работы с файлами и сетью выполняются в синхронном режиме (никаких overlapped).


Однопоточный вариант реализации (концепт коллеги):
1) Главный поток:
    а) цикл ожидания оконных событий (кнопка Exit, завершение работы ОС и т.п.).
    б) ожидание наших событий, включая события от наших таймеров.
    в) поэтапное выполнение блоков кода всех 3-х задач, описанных выше.

После вызова GetMessage принимается решение об исполнении определённого блока кода.
Имеется таймер T1 с периодом 500 мс, на события которого происходит коннект и скачивание данных с текущего FTP-сервера. Когда заканчивается скачивание файла с последнего FTP-сервера, в переменной gt запоминается текущее время. Таймер T1 не начнёт работать с FTP#1 до тех пор, пока разница между текущим временем и gt не превысит 5000 мс.
В данном варианте следует отказаться от использования синхронных операций и стараться использовать именно асинхронные аналоги.
Видимо от обычного connect() стоит перейти к асинхронному ConnectEx(...,overlapped). И тогда, видимо, придётся использовать функцию BindIoCompletionCallback, которая позволит указать callback функцию. В этой callback функции нужно будет вызвать PostThreadMessage, событие которого должно сигнализировать о выполнении функции ConnectEx и о переходе к выполнению функции WSASend (следующий блок кода).
C WSASend практически такая же история как с ConnectEx. Т.е. если вызов WSASend выполнился не сразу, а началась асинхронная операция, то нужно перейти сразу на GetMessage, но не забыть об отслеживании окончания WSASend. При завершении операции отправки поставить в оконную очередь событие и перейти к следующему шагу выполнения.
Следующим шагом будет уже WSARecv, с которым абсолютно такая же история.
Если после обработки полученных с FTP-сервера данных требуется архивация результата, то архивация производится либо в Главном потоке, либо в Дополнительно созданном потоке.
На этапе отдачи результатов на наш FTP-сервер тоже придётся повозиться с ConnectEx, WSASend, WSARecv (нужно обеспечить срабатывание GetMessage на наши события, по которым можно понять на какой блок кода прыгать).
Работа с файлами, по хорошему, должна быть тоже асинхронной (только уже придётся помучаться с ReadFileEx и WriteFileEx).
Также в некоторых местах, возможно, будет необходима элементарная задержка (без неё в некоторых случаях просто никак), но вызвать Sleep не получится (мы же не хотим остановить все подсистемы приложения). В этом случае нужно создавать таймер и по его срабатыванию переходить на следующий блок кода.




Прототипы обоих вариантов уже разработаны и основные задачи обе реализации уже выполняют. Но нужно выбрать один вариант развития ядра системы.
Вопрос: Какое ядро системы Вы бы сделали — многопоточное или однопоточное?
(и вкратце напишите чем противоположный вариант вас не устроил)
многопоточность однопоточность архитектура ядро концепция
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.