Нужны RAII-блокировки, безопасное добавление и фильтрация без удаления элементов во время индексного обхода. В исходнике исключение оставляет mutex захваченным, а условия фильтра удаляют выбранные типы.
Оптимизация многопоточного менеджера файловых путей
Нужны RAII-блокировки, безопасное добавление и фильтрация без удаления элементов во время индексного обхода. В исходнике исключение оставляет mutex захваченным, а условия фильтра удаляют выбранные типы.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В проекте присутствует класс, отвечающий за хранение и предоставление путей к файлам. Класс получает списки путей от разных потоков и должен корректно обслуживать запросы из остальных потоков. Требуется исправить и улучшить существующий код, не меняя его внешнее поведение.
Сценарий использования:
- Один поток создает файлы на сервере и передаёт их пути в объект класса.
- Другой поток генерирует временные файлы локально и тоже передаёт пути.
- Несколько дополнительных потоков извлекают из класса нужные пути (все файлы, только временные и резервные, только обычные).
Задача – провести рефакторинг и оптимизацию класса, обеспечив корректную работу в многопоточной среде.
# include <vector>
# include <string>
# include <mutex>
using namespace std;
class temporaryGeneratedFilesObserver {
public:
temporaryGeneratedFilesObserver(vector<string> f) {
AddFiles(f);
}
void AddFiles(vector<string> f) noexcept {
size_t i = f.size();
try {
m_mutex.lock();
for (i = 0; i < f.size(); i++) {
m_files.push_back(f[i]);
}
m_mutex.unlock();
} catch (...) {
m_files.erase(m_files.end() - i, m_files.end());
}
}
vector<string> GetTempBackupFiles() {
return GetFilesImpl(Backup | Temp);
}
vector<string> GetRegularFiles() {
return GetFilesImpl(Regular);
}
vector<string> GetAllFiles() {
return GetFilesImpl(All);
}
private:
enum Flags {
Temp = 1,
Backup = 2,
Regular = 4,
All = 7
};
vector<string> GetFilesImpl(uint32_t flags) {
m_mutex.lock();
vector<string> v = m_files;
for (size_t i = 0; i < v.size(); i++) {
if ((flags & Temp) && v[i].substr(v[i].size() - 4) == ".tmp") {
v.erase(v.begin() + i--);
}
if ((flags & Backup) && v[i].substr(v[i].size() - 4) == ".bak") {
v.erase(v.begin() + i--);
}
if ((flags & Regular) &&
v[i].substr(v[i].size() - 4) != ".bak" &&
v[i].substr(v[i].size() - 4) != ".tmp") {
v.erase(v.begin() + i--);
}
}
m_mutex.unlock();
return v;
}
mutex m_mutex;
vector<string> m_files;
};
Ответ
Нужны RAII-блокировки, безопасное добавление и фильтрация без удаления элементов во время индексного обхода. В исходнике исключение оставляет mutex захваченным, а условия фильтра удаляют выбранные типы.
Вариант ключевых методов для C++20; существующие Flags и публичные getters сохраняются:
void AddFiles(std::vector<std::string> files) noexcept {
try {
std::lock_guard<std::mutex> lock(m_mutex);
auto updated = m_files;
updated.insert(updated.end(), files.begin(), files.end());
m_files.swap(updated);
} catch (...) {
// Сохраняем прежнее состояние и существующий noexcept-контракт.
}
}
std::vector<std::string> GetFilesImpl(std::uint32_t flags) {
std::vector<std::string> snapshot;
{
std::lock_guard<std::mutex> lock(m_mutex);
snapshot = m_files;
}
std::erase_if(snapshot, [flags](const std::string& name) {
const auto kind = name.ends_with(".tmp") ? Temp
: name.ends_with(".bak") ? Backup : Regular;
return (flags & kind) == 0;
});
return snapshot;
}
Добавление либо заменяет весь список, либо оставляет его неизменным. Фильтрация идёт по локальному снимку без mutex и корректна для коротких имён. Цена строгой гарантии — копия при добавлении. Если требуется сообщать об ошибках, отдельно согласуйте изменение noexcept/результата; молчаливый отказ сохранён лишь ради старой сигнатуры.