Добрый день, дорогие форумчане!
Есть файл со списком из 10 000 записей "ip+port".
Нужно мониторить состояние подключения по адресу. Реализовать чек "жив ли сервер", фактически.
И оповещать — выводить в консоль и файл запись
"ts + address + up/down".
Всё это нужно делать быстро.
1ая реализация
epoll + main thread + output
Придумал, что можно в epoll получать список fd, у которых случилось событие "соединение упало/соединение установлено".
В моменте выводить информацию. Идти к следующему fd, ...
Минус реализации, что проходимся в цикле по выданному пулу fd синхронно записывая в консоль, файл. А в это время уже события на fd новые могут придти.
2ая реализация
epoll + threads + background output
Создаём пулл потоков с theads_number = hardware_concurrency. Чтобы вышло сколько ядер, столько и потоков. Честное распараллеливание.
К примеру, будет 4.
class ThreadPool {
using Task = std::future<void>;
public:
explicit ThreadPool(std::size_t threads_num);
~ThreadPool();
template <typename F, typename ... Args>
void addTask(F f, Args&& ... args) {
{
std::lock_guard<std::mutex> l(cv_m_);
tasks_.emplace(std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...));
}
condition_.notify_one();
}
private:
std::mutex cv_m_;
std::condition_variable_any condition_;
std::queue<Task> tasks_;
std::vector<std::jthread> workers_;
};
ThreadPool::ThreadPool(std::size_t threads_num)
{
for (std::size_t i = 0; i < threads_num; ++i) {
workers_.emplace_back(
[this](std::stop_token stop_token){
while (true) {
std::unique_lock<std::mutex> lk(this->cv_m_);
condition_.wait(lk, stop_token, [this](){
return !this->tasks_.empty();
});
if (stop_token.stop_requested()) {
return;
}
if (!this->tasks_.empty()) {
auto f = std::move(this->tasks_.front());
this->tasks_.pop();
lk.unlock();
f.get();
}
}
}
);
}
}
Получаем от epoll список fd. Делим его на 4 — получаем 4 списка. Каждый отдаём на обработку в пул потоков.
Минус в том, что сгородили целый пулл потоков ради всего-лишь вывода в файл. И файл-то один. Нужно же синхронизировать к нему доступ. Через мьютекс? Вся многопоточность убьётся об него.
Какие ещё могут быть идеи?
Подумал над memory mapped file + спин лок. Спин локом защищаем общую переменную — смещение. Поток подготовил строку для вставки в файл. Теперь ему нужно узнать по какому смещению её записать.
Он лочит спин лок, сохраняет себе смещение, обновляет его — прибавляет к нему длину строки, которую сейчас вставит. Отпускает спин лок. Вставляет по полученному смещению строку.
Т.е. критическая секция получилась маленькая.