а также сделать так, чтобы PieceStorage можно было...

Creato il: 13 maggio 2026

Risposto usando GPT-5.5 Thinking da Chat01

Domanda

а также сделать так, чтобы PieceStorage можно было использовать из нескольких потоков.

#pragma once
#include "torrent_file.h"
#include "piece.h"
#include <queue>
#include <string>
#include <unordered_set>
#include <mutex>
#include <stdexcept>
/*

  • Хранилище информации о частях скачиваемого файла.

  • В этом классе отслеживается информация о том, какие части файла осталось скачать
    /
    class PieceStorage {
    public:
    explicit PieceStorage(const TorrentFile& tf) : totalPieces_(tf.pieceHashes.size()) {
    for (int i = 0; i < tf.pieceHashes.size(); ++i) {
    size_t length = tf.pieceLength;
    if (i == tf.pieceHashes.size() - 1 && tf.length % tf.pieceLength != 0) {
    length = tf.length % tf.pieceLength;
    }
    auto piece = std::make_shared<Piece>(i, length, tf.pieceHashes[i]);
    remainPieces_.push(piece);
    }
    }
    /

    • Отдает указатель на следующую часть файла, которую надо скачать
      /
      PiecePtr GetNextPieceToDownload() {
      std::unique_lock lock(mt_);
      ++pieceInProgress_;
      if (remainPieces_.empty()) {
      throw std::runtime_error("Queue is empty");
      }
      return remainPieces_.front();
      }
      /
    • Эта функция вызывается из PeerConnect, когда скачивание одной части файла завершено.
    • В рамках данного задания требуется очистить очередь частей для скачивания как только хотя бы одна часть будет успешно скачана.
      /
      void PieceProcessed(const PiecePtr& piece) {
      std::unique_lock lock(mt_);
      --pieceInProgress_;
      SavePieceToDisk(piece);
      pieces_.push_back(piece->GetIndex());
      while (!remainPieces_.empty()) {
      remainPieces_.pop();
      }
      }
      /
    • Остались ли нескачанные части файла?
      /
      bool QueueIsEmpty() const {
      std::unique_lock lock(mt_);
      return remainPieces_.empty();
      }
      /
    • Сколько частей файла всего
      /
      size_t TotalPiecesCount() const {
      std::unique_lock lock(mt_);
      return totalPieces_;
      }
      /
    • Сколько частей файла было сохранено на диск
      */
      size_t PiecesSavedToDiscCount() const {
      std::unique_lock lock(mt_);
      return totalPieces_ - remainPieces_.size();
      }

    /*

    • Закрыть поток вывода в файл
      */
      void CloseOutputFile();

    /*

    • Отдает список номеров частей файла, которые были сохранены на диск
      */
      const std::vector<size_t>& GetPiecesSavedToDiscIndices() const {
      return pieces_;
      }

    /*

    • Сколько частей файла в данный момент скачивается
      */
      size_t PiecesInProgressCount() const {
      return pieceInProgress_;
      }
      protected:
      std::queue<PiecePtr> remainPieces_;
      std::vector <size_t> pieces_;
      virtual void SavePieceToDisk(PiecePtr piece);
      long long totalPieces_ = 0, pieceInProgress_ = 0;
      std::mutex mt_;

};
найди ошибки и исправь минимально. стиль кода не меняй! это ВАжно! ТЕ ЖЕ названия переменных, функций, библиотеки, без проспуков и пустых строк.
дай точечные исправления (что на что поменять) с комментариями что не верно у меня
дай готовый верный код В МОЕМ СТИЛЕ  

Pensiero

Я пройдусь точечно: здесь есть и компиляционные ошибки, и логическая ошибка, из-за которой один и тот же кусок могут получить несколько потоков. Исправления будут минимальными, без переименований и без перестройки стиля.

Thought for 28s

Risposta

Ниже точечные исправления.

1. Не хватает заголовков

У тебя используются std::vector и std::make_shared, но явно не подключены нужные библиотеки.

Было:

cpp
#include <stdexcept>

Стало:

cpp
#include <stdexcept> #include <vector> #include <memory>

2. const-методы не смогут залочить std::mutex

В QueueIsEmpty() const, TotalPiecesCount() const, PiecesSavedToDiscCount() const ты делаешь lock(mt_), но mt_ внутри const-метода считается const. Обычный std::mutex так нельзя лочить.

Было:

cpp
std::mutex mt_;

Стало:

cpp
mutable std::mutex mt_;

3. GetNextPieceToDownload() сначала увеличивает счетчик, а потом проверяет очередь

Если очередь пустая, будет исключение, но pieceInProgress_ уже увеличен. Это ошибка.

Еще важнее: ты возвращаешь front(), но не делаешь pop(). Значит несколько потоков могут получить один и тот же Piece.

Было:

cpp
std::unique_lock lock(mt_); ++pieceInProgress_; if (remainPieces_.empty()) { throw std::runtime_error("Queue is empty"); } return remainPieces_.front();

Стало:

cpp
std::unique_lock lock(mt_); if (remainPieces_.empty()) { throw std::runtime_error("Queue is empty"); } auto piece = remainPieces_.front(); remainPieces_.pop(); ++pieceInProgress_; return piece;

4. PiecesSavedToDiscCount() считает неправильно

После исправления GetNextPieceToDownload() части удаляются из remainPieces_ еще до сохранения на диск. Поэтому totalPieces_ - remainPieces_.size() больше не означает “сохранено на диск”.

Надо считать по pieces_, потому что туда ты добавляешь индекс только после SavePieceToDisk(piece).

Было:

cpp
return totalPieces_ - remainPieces_.size();

Стало:

cpp
return pieces_.size();

5. GetPiecesSavedToDiscIndices() небезопасен для нескольких потоков

Сейчас ты возвращаешь ссылку на pieces_ без блокировки. Даже если добавить lock внутри функции, ссылка останется жить после unlock, и другой поток сможет изменить pieces_.

Минимально правильный вариант — вернуть копию.

Было:

cpp
const std::vector<size_t>& GetPiecesSavedToDiscIndices() const { return pieces_; }

Стало:

cpp
std::vector<size_t> GetPiecesSavedToDiscIndices() const { std::unique_lock lock(mt_); return pieces_; }

6. PiecesInProgressCount() читает поле без mutex

Это data race.

Было:

cpp
size_t PiecesInProgressCount() const { return pieceInProgress_; }

Стало:

cpp
size_t PiecesInProgressCount() const { std::unique_lock lock(mt_); return pieceInProgress_; }

Готовый код в твоем стиле:

cpp
#pragma once #include "torrent_file.h" #include "piece.h" #include <queue> #include <string> #include <unordered_set> #include <mutex> #include <stdexcept> #include <vector> #include <memory> /* * Хранилище информации о частях скачиваемого файла. * В этом классе отслеживается информация о том, какие части файла осталось скачать */ class PieceStorage { public: explicit PieceStorage(const TorrentFile& tf) : totalPieces_(tf.pieceHashes.size()) { for (int i = 0; i < tf.pieceHashes.size(); ++i) { size_t length = tf.pieceLength; if (i == tf.pieceHashes.size() - 1 && tf.length % tf.pieceLength != 0) { length = tf.length % tf.pieceLength; } auto piece = std::make_shared<Piece>(i, length, tf.pieceHashes[i]); remainPieces_.push(piece); } } /* * Отдает указатель на следующую часть файла, которую надо скачать */ PiecePtr GetNextPieceToDownload() { std::unique_lock lock(mt_); if (remainPieces_.empty()) { throw std::runtime_error("Queue is empty"); } auto piece = remainPieces_.front(); remainPieces_.pop(); ++pieceInProgress_; return piece; } /* * Эта функция вызывается из PeerConnect, когда скачивание одной части файла завершено. * В рамках данного задания требуется очистить очередь частей для скачивания как только хотя бы одна часть будет успешно скачана. */ void PieceProcessed(const PiecePtr& piece) { std::unique_lock lock(mt_); --pieceInProgress_; SavePieceToDisk(piece); pieces_.push_back(piece->GetIndex()); while (!remainPieces_.empty()) { remainPieces_.pop(); } } /* * Остались ли нескачанные части файла? */ bool QueueIsEmpty() const { std::unique_lock lock(mt_); return remainPieces_.empty(); } /* * Сколько частей файла всего */ size_t TotalPiecesCount() const { std::unique_lock lock(mt_); return totalPieces_; } /* * Сколько частей файла было сохранено на диск */ size_t PiecesSavedToDiscCount() const { std::unique_lock lock(mt_); return pieces_.size(); } /* * Закрыть поток вывода в файл */ void CloseOutputFile(); /* * Отдает список номеров частей файла, которые были сохранены на диск */ std::vector<size_t> GetPiecesSavedToDiscIndices() const { std::unique_lock lock(mt_); return pieces_; } /* * Сколько частей файла в данный момент скачивается */ size_t PiecesInProgressCount() const { std::unique_lock lock(mt_); return pieceInProgress_; } protected: std::queue<PiecePtr> remainPieces_; std::vector <size_t> pieces_; virtual void SavePieceToDisk(PiecePtr piece); long long totalPieces_ = 0, pieceInProgress_ = 0; mutable std::mutex mt_; };

Еще важно: если CloseOutputFile() в .cpp трогает общий файловый поток, его реализацию тоже надо защищать этим же mt_.

Condividi questa Q&A