Linux环境下,多线程统计txt文件中的单词词频

#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函数我们可以看到,单线程是快于多线程的,并且多线程系统用时远高于单线程,因为我们的设计思路是一个线程统计一个文件中的单词数,所以如果输入文件数越多,线程之间上下文切换的系统消耗就越多。所以可以设想一下,如果输入文件达到上百个时,多线程和单线程的结果是相同的,但是多线程无论是时间还是系统的消耗都是极大高于单线程的

上一篇:PCL估计VFH特征


下一篇:sys模块