【问题标题】:C++ : How to read text file in chunks and run functions on each chunk?C ++:如何读取块中的文本文件并在每个块上运行函数?
【发布时间】:2022-01-21 09:53:50
【问题描述】:

我想从本地存储读取一个文本文件,我正在尝试多处理,所以我想将文本文件分成更小的块并在它们上运行一个进程。

粗略的想法

输入:10Kb 文本文件

程序将它们分成每个 1Kb 的块

分别在每个块上运行一个函数(例如:大写某些字符,查找字母的频率或搜索该块中的单词)

输出:返回没有内存泄漏或读取不匹配的函数输出

我尝试使用 pread,但我在 Windows 上,所以任何解决方案或解决此问题的线索都会有所帮助

【问题讨论】:

  • 你试过什么?你的尝试有minimal reproducible example 吗?您的尝试遇到了什么问题?
  • 在C++中读取文件,可以使用std::ifstream
  • 另请注意,可变长度文件(如文本文件)通常不能在静态“块”中得到很好的处理。您无法判断“块”是否在单词、句子或文件中分隔记录的任何内容上分割。您应该如何处理该文件及其内容?
  • 这是我目前的进展,我不确定第三个文件,因为我在网上的某个线程上找到了它。 codeshare我想合并第一个和第二个文件逻辑来打破文本文件块并在它们上运行一个函数
  • 请花一些时间刷新the help pages,采取SO tour,阅读How to Ask,以及this question checklist。最后请不要忘记如何edit您的问题,例如将您的minimal reproducible example复制粘贴为文本以及您遇到的问题的描述。

标签: c++ windows multithreading file memory


【解决方案1】:

也许你选择了错误的例子来学习多线程。

存储在顺序驱动器上的文件在顺序模式下读取速度最快。

因此,在下面的示例中,我将把完整的文件一次性读入一个字符串。出于测试目的,我使用了“Lorem Ipsum”生成器并创建了一个包含 100 万个字符的文件。 100 万现在被认为仍然很小。

出于演示目的,我将创建 4 个并行线程。

将这个完整的文件放在一个字符串中后,我会将大字符串拆分为 4 个子字符串。每个线程一个。

对于线程函数,我创建了一个 4 行测试函数,用于计算给定子字符串的字母数。

为了便于学习,我将使用std::async 来创建线程。 std::async 的结果将存储在 std::future 中。稍后我们可以在那里获取测试功能结果。我们需要使用shared_future 才能将它们全部存储在std::array 中,因为std::future 的复制构造函数被删除了。

然后,我们让线程完成它们的工作。

在一个额外的循环中,我们使用futuresget函数,它将等待线程完成然后给我们结果。

我们将所有 4 个线程的值相加,然后以排序方式打印出来。请注意:\n 也会被计算在内,这在输出中会看起来有点奇怪。

请注意。这只是演示。它甚至比直接的解决方案还要慢。这只是为了展示hwo多线程可以工作。

请看下面一个简单的例子(许多可能的解决方案之一):

#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <iterator>
#include <future>
#include <thread>
#include <array>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;

// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;

// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------

// We will use 4 threads for our task
constexpr size_t NumberOfThreads = 4u;

// Some test function used by a thread. Count characters in text
Counter countCharacters(const std::string& text) {

    // Definition of the counter
    Counter counter{};

    // Count all letters
    for (const char c : text) counter[c]++;

    // Give back result
    return counter;
}

// Test / driver Code
int main() {
    // Open a test file with 1M characters and check, if it could be opened
    if (std::ifstream sourceStream{ "r:\\text.txt" }; sourceStream) {

        // Read the complete 1M file into a string
        std::string text(std::istreambuf_iterator<char>(sourceStream), {});
        

        // ------------------------------------------------------------------------------------------------
        // This is for the multhreading part
        
        // We will split the big string in parts and give each thread the task to work with this part
        // Calculate the length of one partition + some reserve in case of rounding problem
        const size_t partLength = text.length() / NumberOfThreads + NumberOfThreads;
        
        // We will create numberOfThread Substrings starting at equidistant positions. This is the start.
        size_t threadStringStartpos = 0;

        // Container for the futures. Please note. We can only use shared futures in containers.
        std::array<std::shared_future<Counter>, NumberOfThreads> counter{};

        // Now create the threats
        for (unsigned int threadNumber{}; threadNumber < NumberOfThreads; ++threadNumber) {
        
            // STart a thread. Get a reference to the future. And call it with our test function and a part of the string
            counter[threadNumber] = std::async( countCharacters, text.substr(threadStringStartpos, partLength));

            // Calculate next part of string
            threadStringStartpos += partLength;
        }
        // Combine results from threads
        Counter result{};
        for (unsigned int threadNumber{}; threadNumber < NumberOfThreads; ++threadNumber) {

            // Get will get the result from the thread via the assigned future
            for (const auto& [letter, count] : counter[threadNumber].get())
                result[letter] += count;    // Sum up all counts
        }
        // ------------------------------------------------------------------------------------------------

        for (const auto& [letter, count] : Rank(result.begin(), result.end())) std::cout << letter << " --> " << count << '\n';
    }
    else std::cerr << "\n*** Error: Could not open source file\n";
}

【讨论】:

  • 这是一个非常优雅的解决方案,但我认为将整个文件读取为字符串会浪费很多时间。有没有办法直接从文件中逐字符读取?这将大大提高性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-02
  • 2022-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-05
  • 1970-01-01
相关资源
最近更新 更多