面试题答案
一键面试import os
import concurrent.futures
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def write_to_file(data_chunk, file_num):
file_name = f'file{file_num + 1}.txt'
with open(file_name, 'w') as f:
for line in data_chunk:
f.write(line + '\n')
def distribute_data(data, num_files):
chunk_size = len(data) // num_files
data_chunks = [data[i * chunk_size: (i + 1) * chunk_size] for i in range(num_files)]
# 处理余数
if len(data) % num_files != 0:
data_chunks[-1].extend(data[num_files * chunk_size:])
return data_chunks
def parallel_write(data, num_files, use_processes=False):
data_chunks = distribute_data(data, num_files)
executor_type = ProcessPoolExecutor if use_processes else ThreadPoolExecutor
with executor_type(max_workers=num_files) as executor:
executor.map(write_to_file, data_chunks, range(num_files))
if __name__ == '__main__':
# 生成模拟数据
data = [f'data line {i}' for i in range(10000)]
num_files = 100
parallel_write(data, num_files, use_processes=True)
选择多进程的原因:
-
CPU 密集型任务:当写入大量数据时,磁盘 I/O 虽然是主要操作,但在多核心 CPU 环境下,多进程可以利用多核并行处理能力,提升整体效率。而多线程由于 GIL(全局解释器锁)的存在,在 Python 中无法真正利用多核优势进行并行计算,对于 I/O 密集型任务多线程可能有一定优势,但在此场景下,由于数据量和文件数量较大,多进程的并行处理优势更明显。
-
资源隔离:每个进程都有自己独立的地址空间,这意味着不同进程之间不会相互干扰,能有效避免因共享资源而导致的复杂同步问题,例如全局变量的修改冲突等。
处理资源竞争问题:
-
文件写入:使用
with open
语句来确保文件操作的原子性。在write_to_file
函数中,with open(file_name, 'w') as f
会在操作完成后自动关闭文件,避免了多个进程同时写入同一文件时可能出现的数据错乱。同时,由于每个进程处理不同的数据块并写入不同的文件,不存在对同一文件的资源竞争。 -
数据分配:通过
distribute_data
函数将数据合理分配到不同的数据块中,每个进程负责处理一个数据块并写入对应的文件,保证了数据分配的合理性且避免了数据处理上的竞争。
如果选择多线程,同样使用 with open
语句保证文件写入安全,但需要额外注意 GIL 对效率的影响,并且可能需要使用 Lock
等同步机制来确保线程安全,例如对共享资源(如全局计数器等)的访问。但在本场景下,多线程在效率上不如多进程。