You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++多线程调用Salsa20算法时内存暴涨问题及解决方案问询

解决Salsa20多线程文件处理内存暴涨问题

内存暴涨的核心原因

你遇到的内存暴涨主要是Windows文件系统缓存导致的:多线程同时读写大文件(如ISO镜像)时,Windows会自动将文件数据缓存到物理内存中以提升后续读写速度,这部分内存由系统管理,不属于你的程序堆/栈内存,但会被计入系统整体内存占用。此外,原代码中的频繁lseek+读写操作会加剧系统缓存的累积。

低内存多线程解决方案

1. 绕过Windows文件系统缓存

使用Windows原生API打开文件时,添加FILE_FLAG_NO_BUFFERING和FILE_FLAG_WRITE_THROUGH标志,直接读写磁盘而不经过系统缓存,从根源上避免缓存占用内存。注意:

  • 读写缓冲区大小和文件偏移量必须是磁盘扇区大小的整数倍(通常为4096字节)
  • 缓冲区需对齐到扇区边界

修改后的salsa20函数(替换原POSIX文件API):

#include <windows.h>
#include <thread>
#include <memory>
#include <algorithm>

// 磁盘扇区大小(可通过GetDiskFreeSpace获取实际值)
constexpr DWORD SECTOR_SIZE = 4096;
// 缓冲区大小保持40KB,为扇区大小的整数倍
constexpr size_t BN = SECTOR_SIZE * 10;

// RAII管理文件句柄,避免泄漏
struct FileHandleDeleter {
    void operator()(HANDLE h) const {
        if (h != INVALID_HANDLE_VALUE) CloseHandle(h);
    }
};
using UniqueFileHandle = std::unique_ptr<void, FileHandleDeleter>;

void salsa20(const wchar_t* fileName, const char* key) {
    // 打开文件,禁用系统缓存
    HANDLE hFile = CreateFileW(
        fileName,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        nullptr,
        OPEN_EXISTING,
        FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
        nullptr
    );

    if (hFile == INVALID_HANDLE_VALUE) return;
    UniqueFileHandle fileHandle(hFile);

    // 分配对齐的缓冲区
    uint8_t* temp = reinterpret_cast<uint8_t*>(_aligned_malloc(BN, SECTOR_SIZE));
    if (!temp) return;

    DWORD readSize = 0;
    LARGE_INTEGER filePos = {0};
    int bufsec = 0;

    while (ReadFile(hFile, temp, BN, &readSize, nullptr) && readSize > 0) {
        // 执行Salsa20加密逻辑
        for (int i = 0; i < readSize; i += 64) {
            int chunkSize = std::min(64, readSize - i);
            salsa_encrypt(reinterpret_cast<const uint8_t*>(key), bufsec++, temp + i, chunkSize);
        }

        // 定位到读取位置并写回数据
        SetFilePointerEx(hFile, filePos, nullptr, FILE_BEGIN);
        DWORD writtenSize = 0;
        WriteFile(hFile, temp, readSize, &writtenSize, nullptr);

        // 更新文件位置,准备下一次读取
        filePos.QuadPart += readSize;
        SetFilePointerEx(hFile, filePos, nullptr, FILE_BEGIN);
    }

    _aligned_free(temp);
}

2. 优化线程资源管理

  • 限制并发数:磁盘(尤其是HDD)的并发IO能力有限,过多线程会导致磁头寻道开销增加,反而降低效率。建议并发数等于CPU核心数或磁盘IO队列长度,使用线程池统一管理:
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>

class ThreadPool {
public:
    ThreadPool(size_t numThreads) {
        for (size_t i = 0; i < numThreads; ++i) {
            threads.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [this] { return stop || !tasks.empty(); });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (auto& t : threads) t.join();
    }

    void enqueue(std::function<void()> task) {
        {
            std::unique_lock<std::mutex> lock(mtx);
            tasks.emplace(std::move(task));
        }
        cv.notify_one();
    }

private:
    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
};

int main() {
    // 根据CPU核心数创建线程池
    ThreadPool pool(std::thread::hardware_concurrency());

    pool.enqueue([] { salsa20(L"1.iso", "PASSWORDTYPE1___!@#$%^&*()!@#$%^"); });
    pool.enqueue([] { salsa20(L"2.iso", "PASSWORDTYPE2___!@#$%^&*()!@#$%^"); });
    pool.enqueue([] { salsa20(L"3.iso", "PASSWORDTYPE2___!@#$%^&*()!@#$%^"); });

    return 0;
}
  • 确保资源正确释放:原代码中read出错时直接break未关闭文件,可能导致资源泄漏,使用RAII(如UniqueFileHandle)可自动管理文件句柄,避免此类问题。

3. 检查加密函数内存泄漏

确认salsa_encrypt函数中没有动态内存分配未释放的情况(如malloc/new后未free/delete),或全局变量累积内存的问题。

额外建议

  • 若使用MinGW/Cygwin编译,需确保链接Windows系统库(如-luser32 -lkernel32)
  • 不要强制释放系统缓存(如SetSystemFileCacheSize),该操作需要管理员权限,且可能影响其他程序性能

内容的提问来源于stack exchange,提问作者Melvin Lang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 19:00:53