程序设计十:进程和线程
一、Map进程
定义map
函数,对文本进行分词,并计算批量文本的词频统计结果,其中调用_statword
函数来统计每个词语的词频,代码如下:
def map(queue, content_i):
# 计算批量词频统计的结果
print('map {} start...'.format(os.getpid()))
all_words = []
for text in content_i:
words = jieba.lcut(text)
all_words.extend(words)
word_freq_i = _statword(all_words)
queue.put(word_freq_i)
queue.join()
def _statword(all_words):
'''
统计词频
:param all_words: 列表类型,包含分词
:return: 字典类型,值为非停用词的词频
'''
freq = {}
# freq = collections.defaultdict(default_value()) # 若下文不用 .get(word,0) 方法,则可以用此句初始化字典
# 载入停用词
stopwords = [line.strip() for line in open('stopwords_list.txt', 'r', encoding='utf-8').readlines()]
# 统计词频
for word in all_words:
if word not in stopwords and word != ' ':
freq[word] = freq.get(word, 0) + 1
return freq
二、Reduce进程
定义reduce
函数,根据队列获取的批量统计,更新全局词频统计,其中_update
函数更新全局词频统计,代码如下:
def reduce(queue, word_freq):
# 根据队列获取的批量统计,更新全局词频统计
word_freq_i = queue.get()
if word_freq_i == None:
print('No word, {} end'.format(os.getpid()))
else:
word_freq = _update(word_freq, word_freq_i)
print('reduce {} start...'.format(os.getpid()))
queue.task_done()
def _update(word_freq, word_freq_i):
for key in word_freq_i.keys():
if key in word_freq.keys():
word_freq[key] += word_freq_i[key]
else:
word_freq[key] = word_freq_i[key]
return word_freq
三、进程建立与启动
在if __name__ == '__main__':
中,建立Map
进程和Reduce
进程,将文本切片分给各个Map
进程,再由Map
进程统计后,添加到队列中,Reduce
进程从队列中获取统计结果,并更新全局结果
# 以下代码均在 if __name__ == '__main__': 中
for i in range(proc_num):
# 根据文本量,判断每个进程需要读入多少文本
content_i = content[
i * LEN // proc_num:(i + 1) * LEN //proc_num].copy()
print('----建立第{}/{}个进程----'.format(i+1,proc_num))
#print(f'要处理的文本如下\n{content_i[:3]}')
Map = Process(target=map, args=(queue, content_i,)) # 统计批量词频
map_lis.append(Map) # 记录批量词频的结果
for j in range(proc_num):
Reduce = Process(target=reduce, args=(queue, word_freq,)) # 更新词频
Reduce.daemon=True
reduce_lis.append(Reduce) # 记录更新后总词频
#print('------ start map ------')
for Map in map_lis:
Map.start()
#print('---start reduce---')
for Reduce in reduce_lis:
Reduce.start()
for Map in map_lis:
Map.join()
for Map in map_lis:
queue.put(None)
for Reduce in reduce_lis:
Reduce.join()
四、运行结果与不同进程数运行时间比较
测试进程数在[1,10]之间的运行时间,结果如下:
绘制图像可得到如下结果(运行三次):
可以观察到如下结论:
- 在本次测试范围内,随着进程数量的增加,总运行时间呈现下降趋势,这是因为进程数增加增多个进程并行,会加快执行效率,减少用时;
- 在本次测试范围内,随着进程数的增多,运行时间的减小趋势越来越小,边际效用递减,这是因为建立进程也会耗时,以及除并行部分外还有其他消耗资源的因素,导致边际递减
- 运行时长并不一直下降,而是略有起伏