也许你选择了错误的例子来学习多线程。
存储在顺序驱动器上的文件在顺序模式下读取速度最快。
因此,在下面的示例中,我将把完整的文件一次性读入一个字符串。出于测试目的,我使用了“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";
}