Multithreading 使用对象打开多个线程并返回结果

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::

我试图通过一个循环打开多个线程,其中每个线程都是一个类的实例,该类的构造函数被重载,这样它就会自动运行所需的代码。这个函数返回一个无序的_列表,我想检索它,然后将该特定实例附加到最终的无序_列表中

我尝试过使用未来和承诺,但当我尝试时,我最终把自己弄糊涂了。这个项目的目的是挑战我,帮助我学习C++中的多线程。
    //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
类的上下文中,name
First
将表示pair类的
First
参数,name
Second
将表示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;
}