Multithreading 使用对象打开多个线程并返回结果
我试图通过一个循环打开多个线程,其中每个线程都是一个类的实例,该类的构造函数被重载,这样它就会自动运行所需的代码。这个函数返回一个无序的_列表,我想检索它,然后将该特定实例附加到最终的无序_列表中 我尝试过使用未来和承诺,但当我尝试时,我最终把自己弄糊涂了。这个项目的目的是挑战我,帮助我学习C++中的多线程。Multithreading 使用对象打开多个线程并返回结果,multithreading,oop,c++11,std,unordered-map,Multithreading,Oop,C++11,Std,Unordered Map,我试图通过一个循环打开多个线程,其中每个线程都是一个类的实例,该类的构造函数被重载,这样它就会自动运行所需的代码。这个函数返回一个无序的_列表,我想检索它,然后将该特定实例附加到最终的无序_列表中 我尝试过使用未来和承诺,但当我尝试时,我最终把自己弄糊涂了。这个项目的目的是挑战我,帮助我学习C++中的多线程。 //class to be instantiated per thread class WordCounter { public: std::
//class to be instantiated per thread
class WordCounter {
public:
std::unordered_map<std::string, int> thisWordCount;
std::string word;
WordCounter(std::string filepath) {}//will be overloaded
~WordCounter() {}//destructor
std::unordered_map<std::string, int>operator()(std::string filepath) const {}//overloaded constructor signature
std::unordered_map<std::string, int>operator()(std::string currentFile) {//overloaded constructor implementation
fstream myReadFile;
myReadFile.open(currentFile);
if (!!!myReadFile) {
cout << "Unable to open file";
exit(1); // terminate with error
}
else if (myReadFile.is_open()) {
while (!myReadFile.eof()) {
while (myReadFile >> word) {
++thisWordCount[word];
}
}
}
myReadFile.close();
return thisWordCount;
}
};
int main(int argc, char** argv)
{
std::vector<std::thread> threads;//store instantiated threads using WordCounter
static std::unordered_map<std::string, int> finalWordCount; //append result from each thread to this unordered_list only when a particular thread finish's reading a file
vector<string> fileName = { "input1.txt" , "input2.txt" };//filepaths to the files used
for (int i = 0; i < fileName.size(); ++i)//loop through vector of filepaths to open a thread for each file to then be processed by that thread
{
std::string currentFile = DIR + fileName[i];
std::thread _newThread(new WordCount(currentFile); //is this how the thread would be created?
threads.emplace_back(_newThread);//store new thread in a vector
//I want to read through the vector when a particular thread finishes and append that particular threads result to finalWordCount
}
//每个线程要实例化的类
类字计数器{
公众:
std::无序映射thisWordCount;
字符串字;
WordCounter(std::string filepath){}//将被重载
~WordCounter(){}//析构函数
std::无序映射运算符()(std::string filepath)常量{}//重载构造函数签名
std::无序映射运算符()(std::string currentFile){//重载构造函数实现
fstream-myReadFile;
myReadFile.open(当前文件);
如果(!!!myReadFile){
cout>word){
++这个字数[字];
}
}
}
myReadFile.close();
返回此wordcount;
}
};
int main(int argc,字符**argv)
{
std::vector threads;//使用WordCounter存储实例化线程
static std::unordered_map finalWordCount;//仅当特定线程完成读取文件时,才将每个线程的结果追加到此unordered_列表
向量文件名={“input1.txt”,“input2.txt”};//所用文件的文件路径
for(int i=0;i
}多线程处理您的代码
让我们首先编写一个多线程的countWords
函数。这将为我们提供代码需要做什么的高级概述,然后我们将填充缺少的部分
写countWords
countWords
以文件名向量计算每个文件中的单词频率。它并行执行此操作
步骤概述:
using std::unordered_map;
using std::string;
using std::vector;
unordered_map<string, int> countWords(vector<string> const& filenames) {
// Create vector of threads
vector<std::thread> threads;
threads.reserve(filenames.size());
// We have to have a lock because maps aren't thread safe
std::mutex map_lock;
// The final result goes here
unordered_map<std::string, int> totalWordCount;
// Define the callback function
// This operation is basically free
// Internally, it just copies a reference to the mutex and a reference
// to the totalWordCount
auto callback = [&](unordered_map<string, int> const& partial_count) {
// Lock the mutex so only we have access to the map
map_lock.lock();
// Update the map
for(auto count : partial_count) {
totalWordCount[count.first] += count.second;
}
// Unlock the mutex
map_lock.unlock();
};
// Create a new thread for each file
for(auto& file : filenames) {
auto word_counter = makeWordCounter(callback);
threads.push_back(std::thread(word_counter, file));
}
// Wait until all threads have finished
for(auto& thread : threads) {
thread.join();
}
return totalWordCount;
}
- 创建一个线程向量
- 提供存储最终结果的位置(这是
变量)finalWordCount
- 为
创建一个回调函数,以便在完成时调用WordCounter
- 使用
对象为每个文件启动一个新线程WordCounter
- 等待Thead完成
- 返回
finalWordCount
WordCounter
对象将文件名作为输入
缺少的部分:
using std::unordered_map;
using std::string;
using std::vector;
unordered_map<string, int> countWords(vector<string> const& filenames) {
// Create vector of threads
vector<std::thread> threads;
threads.reserve(filenames.size());
// We have to have a lock because maps aren't thread safe
std::mutex map_lock;
// The final result goes here
unordered_map<std::string, int> totalWordCount;
// Define the callback function
// This operation is basically free
// Internally, it just copies a reference to the mutex and a reference
// to the totalWordCount
auto callback = [&](unordered_map<string, int> const& partial_count) {
// Lock the mutex so only we have access to the map
map_lock.lock();
// Update the map
for(auto count : partial_count) {
totalWordCount[count.first] += count.second;
}
// Unlock the mutex
map_lock.unlock();
};
// Create a new thread for each file
for(auto& file : filenames) {
auto word_counter = makeWordCounter(callback);
threads.push_back(std::thread(word_counter, file));
}
// Wait until all threads have finished
for(auto& thread : threads) {
thread.join();
}
return totalWordCount;
}
- 我们仍然需要编写一个
函数makeWordCounter
using std::unordered_map;
using std::string;
using std::vector;
unordered_map<string, int> countWords(vector<string> const& filenames) {
// Create vector of threads
vector<std::thread> threads;
threads.reserve(filenames.size());
// We have to have a lock because maps aren't thread safe
std::mutex map_lock;
// The final result goes here
unordered_map<std::string, int> totalWordCount;
// Define the callback function
// This operation is basically free
// Internally, it just copies a reference to the mutex and a reference
// to the totalWordCount
auto callback = [&](unordered_map<string, int> const& partial_count) {
// Lock the mutex so only we have access to the map
map_lock.lock();
// Update the map
for(auto count : partial_count) {
totalWordCount[count.first] += count.second;
}
// Unlock the mutex
map_lock.unlock();
};
// Create a new thread for each file
for(auto& file : filenames) {
auto word_counter = makeWordCounter(callback);
threads.push_back(std::thread(word_counter, file));
}
// Wait until all threads have finished
for(auto& thread : threads) {
thread.join();
}
return totalWordCount;
}
编写WordCounter
类
成员变量:
- 回调函数(我们不需要其他任何东西)
使用文件名调用operator()
countWordsFromFilename
打开文件,确保其正常,并使用文件流调用countWordsFromFilename
countWords
读取文件流中的所有单词并计算计数,然后使用最终计数调用回调countWords
WordCounter
非常简单,所以我把它做成了一个结构。它只需要存储Callback
函数,并且通过将Callback
函数公开,我们不必编写构造函数(编译器使用聚合初始化自动处理它)
这里,我们将pair
声明为struct
,因为我们希望所有成员都是公共的
注意,没有第一个
类型,也没有第二个
类型。当我们使用第一个
和第二个
名称时,我们真正要说的是“在pair
类的上下文中,nameFirst
将表示pair类的First
参数,nameSecond
将表示pair类的第二个元素
我们可以很容易地把它写成:
// This is completely valid too
template<class A, class B>
struct pair {
A first;
B second;
};
就像普通类一样,pair
可以有成员函数、构造函数、析构函数……任何东西。因为pair
是如此简单的类,编译器会自动为我们生成构造函数和析构函数,我们不必担心它们
模板函数
模板化函数看起来与常规函数类似。唯一的区别是它们在函数声明的其余部分之前有模板
声明
让我们编写一个简单的函数来打印一对:
template<class A, class B>
std::ostream& operator<<(std::ostream& stream, pair<A, B> pair)
{
stream << '(' << pair.first << ", " << pair.second << ')';
return stream;
}
这将输出(14,你好,世界)
回调
< C++中没有<代码>回调< /代码>。我们不需要一个。回调只是用来指示发生了什么。
让我们看一个简单的例子。这个函数查找逐渐变大的数字,每次找到一个,它都会调用output
,这是我们提供的一个参数。在这种情况下,output
是一个回调,我们使用它来指示找到了一个新的最大数字
template<class Func>
void getIncreasingNumbers(std::vector<double> const& nums, Func output)
{
// Exit if there are no numbers
if(nums.size() == 0)
return;
double biggest = nums[0];
// We always output the first one
output(biggest);
for(double num : nums)
{
if(num > biggest)
{
biggest = num;
output(num);
}
}
}
或者我们可以打印出来:
void printNonIncreasing(std::vector<double> const& nums)
{
// Here, we don't put anything in the square brackts
// Since we don't access any local variables
auto my_callback = [](double val) {
std::cout << "New biggest number: " << val << '\n';
};
getIncreasingNums(nums, my_callback);
}
void printNonIncreming(标准::向量常量和nums)
{
//在这里,我们没有把任何东西放在方括号里
//因为我们没有acc
int main() {
// Create pair with an int and a string
pair<int, std::string> myPair{14, "Hello, world!"};
std::cout << myPair << '\n';
}
template<class Func>
void getIncreasingNumbers(std::vector<double> const& nums, Func output)
{
// Exit if there are no numbers
if(nums.size() == 0)
return;
double biggest = nums[0];
// We always output the first one
output(biggest);
for(double num : nums)
{
if(num > biggest)
{
biggest = num;
output(num);
}
}
}
std::vector<double> filterNonIncreasing(std::vector<double> const& nums)
{
std::vector<double> newNums;
// Here, we use an & inside the square brackets
// This is so we can use newNums by reference
auto my_callback = [&](double val) {
newNums.push_back(val);
};
getIncreasingNumbers(nums, my_callback);
return newNums;
}
void printNonIncreasing(std::vector<double> const& nums)
{
// Here, we don't put anything in the square brackts
// Since we don't access any local variables
auto my_callback = [](double val) {
std::cout << "New biggest number: " << val << '\n';
};
getIncreasingNums(nums, my_callback);
}
double findBiggestJumpBetweenIncreasing(std::vector<double> const& nums)
{
double previous;
double biggest_gap = 0.0;
bool assigned_previous = false;
auto my_callback = [&](double val) {
if(not assigned_previous) {
previous = val;
assigned_previous = true;
}
else
{
double new_gap = val - previous;
if(biggest_gap < new_gap) {
biggest_gap = new_gap;
}
}
};
getIncreasingNums(nums, my_callback);
return biggest_gap;
}