#include <thread>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <mutex>
#include <condition_variable>
#include <algorithm>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <atomic>
#include <fstream>
using namespace std;
typedef pair<string, int> PSI;
class Source {// 用于给互斥变量加锁
public:
unordered_map<string, int> mapWords;
mutex m;
unordered_set<string> specialWords;// 存储需要排除在外的单词(例如代词、介词、冠词等)
} source;
atomic_int wordsCount;// 原子整型变量,用于统计单词总数,并且会自动实现互斥访问
vector<PSI> vecWords;// 实现词频排序
bool compare(const PSI& a, const PSI& b) {// 自定义排序规则
return a.second > b.second;
}
void countWords(char* fileName) {// 统计一个文件中的单词
FILE *inputFile = fopen((const char *)fileName, "r");
if (inputFile == NULL) {
puts("No input file");
exit(0);
}
while (true) {
char ch;
string str = "";
while ((ch = fgetc(inputFile)) != EOF) {
if (!isalpha(ch) && str.size() == 0) {// 读到的字符不为字母,且现有字符串长度为0,则直接跳过
continue;
} else if (!isalpha(ch) && str.size() > 0)// 同上,但是字符串长度不为0,现有的字符串是一个完整的单词了
break;
str += tolower(ch);// 正常读入了一个字符,就添加到临时字符串中
}
if (ch == EOF)
break;
else {
unique_lock<mutex> lck(source.m);// 对存储队列加锁
wordsCount++;
if (!source.specialWords.count(str)) {
source.mapWords[str]++;// 将读入的单词加入到统计集
}
lck.unlock();// 解锁
}
}
fclose(inputFile);
}
void fixSpecialWords() {
ifstream in("./wordSet.txt");// wordSet.txt就是存储的需要排除在外的单词
string str;
if (in)
while (getline(in, str))
source.specialWords.insert(str);
else
puts("read error");
}
void showResult() {// 输出结果
cout << "Number of input words: " << wordsCount << endl;
for (auto it : source.mapWords)
vecWords.push_back(it);
sort(vecWords.begin(), vecWords.end(), compare);
int cnt = 0;
if (vecWords.size() >= 10) {
cnt = vecWords[9].second;
for (auto it : vecWords) {
if (it.second < cnt)
break;
cout << it.first << " : " << it.second << endl;
}
} else {
for (auto it : vecWords)
cout << it.first << " : " << it.second << endl;
}
}
int main(int argc, char *argv[]) {
thread th[100];
fixSpecialWords();
for (int i = 1; i < argc; i++)// 将要进行统计的文件名作为参数今传入程序
th[i] = thread(countWords, argv[i]);
for (int i = 1; i < argc; i++)
th[i].join();
showResult();
return 0;
}
c++的main函数中argc 是argument count的缩写表示传入main函数中的参数个数,包括这个程序本身。argv 是 argument vector的缩写表示传入main函数中的参数列表,其中argv[0]表示这个程序的名字,argv[0]指向程序运行时的全路径名;argv[1] 指向程序在命令行中执行程序名后的第一个字符串;argv[2] 指向程序在命令行中执行程序名后的第二个字符串;以此类推直到argv[argc]......
如图tmux中,左边是单线程统计的结果,右边是多线程统计的结果;通过time函数我们可以看到,单线程是快于多线程的,并且多线程系统用时远高于单线程,因为我们的设计思路是一个线程统计一个文件中的单词数,所以如果输入文件数越多,线程之间上下文切换的系统消耗就越多。所以可以设想一下,如果输入文件达到上百个时,多线程和单线程的结果是相同的,但是多线程无论是时间还是系统的消耗都是极大高于单线程的