а также сделать так, чтобы PieceStorage можно было...
बनाया गया: 13 मई 2026
का उपयोग करके उत्तर दिया गया GPT-5.5 Thinking द्वारा Chat01
बनाया गया: 13 मई 2026
का उपयोग करके उत्तर दिया गया GPT-5.5 Thinking द्वारा Chat01
а также сделать так, чтобы 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);
}
}
/
/*
/*
/*
};
найди ошибки и исправь минимально. стиль кода не меняй! это ВАжно! ТЕ ЖЕ названия переменных, функций, библиотеки, без проспуков и пустых строк.
дай точечные исправления (что на что поменять) с комментариями что не верно у меня
дай готовый верный код В МОЕМ СТИЛЕ
Ниже точечные исправления.
У тебя используются std::vector и std::make_shared, но явно не подключены нужные библиотеки.
Было:
cpp#include <stdexcept>
Стало:
cpp#include <stdexcept> #include <vector> #include <memory>
const-методы не смогут залочить std::mutexВ QueueIsEmpty() const, TotalPiecesCount() const, PiecesSavedToDiscCount() const ты делаешь lock(mt_), но mt_ внутри const-метода считается const. Обычный std::mutex так нельзя лочить.
Было:
cppstd::mutex mt_;
Стало:
cppmutable std::mutex mt_;
GetNextPieceToDownload() сначала увеличивает счетчик, а потом проверяет очередьЕсли очередь пустая, будет исключение, но pieceInProgress_ уже увеличен. Это ошибка.
Еще важнее: ты возвращаешь front(), но не делаешь pop(). Значит несколько потоков могут получить один и тот же Piece.
Было:
cppstd::unique_lock lock(mt_); ++pieceInProgress_; if (remainPieces_.empty()) { throw std::runtime_error("Queue is empty"); } return remainPieces_.front();
Стало:
cppstd::unique_lock lock(mt_); if (remainPieces_.empty()) { throw std::runtime_error("Queue is empty"); } auto piece = remainPieces_.front(); remainPieces_.pop(); ++pieceInProgress_; return piece;
PiecesSavedToDiscCount() считает неправильноПосле исправления GetNextPieceToDownload() части удаляются из remainPieces_ еще до сохранения на диск. Поэтому totalPieces_ - remainPieces_.size() больше не означает “сохранено на диск”.
Надо считать по pieces_, потому что туда ты добавляешь индекс только после SavePieceToDisk(piece).
Было:
cppreturn totalPieces_ - remainPieces_.size();
Стало:
cppreturn pieces_.size();
GetPiecesSavedToDiscIndices() небезопасен для нескольких потоковСейчас ты возвращаешь ссылку на pieces_ без блокировки. Даже если добавить lock внутри функции, ссылка останется жить после unlock, и другой поток сможет изменить pieces_.
Минимально правильный вариант — вернуть копию.
Было:
cppconst std::vector<size_t>& GetPiecesSavedToDiscIndices() const { return pieces_; }
Стало:
cppstd::vector<size_t> GetPiecesSavedToDiscIndices() const { std::unique_lock lock(mt_); return pieces_; }
PiecesInProgressCount() читает поле без mutexЭто data race.
Было:
cppsize_t PiecesInProgressCount() const { return pieceInProgress_; }
Стало:
cppsize_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_.