Оптимизация многопоточного менеджера файловых путей

Нужны 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/результата; молчаливый отказ сохранён лишь ради старой сигнатуры.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее