操作前说明 #

运行示例代码前,建议先确认 Python 版本。旧文章中可能包含 Python 2 写法,如果当前环境是 Python 3,需要注意 print、字符串编码、包安装方式和模块路径差异。

原始操作记录 #

废话不多说,直接上代码。使用了两个包,futures 和 multiprocessing,支持 ThreadPool 和 ProcessPool。

怎么区分进程和线程呢,简单的说就是多进程可以在任务列表里看见多个进程,多线程可以在任务列表里看到1个进程有多个线程。

Python
#coding:utf-8
from concurrent import futures
from multiprocessing.pool import ThreadPool
import urllib2
import multiprocessing
import os, sys

URLS = [
		'http://www.aspone.me/',
		'http://www.baidu.com/',
		'http://www.openstack.org/',
		'http://www.drcloud.cn/',
		'http://www.163.com/',
		'http://www.microsoft.com/',
		'http://www.python.org/'
	]

def load_url(url, timeout):
	print 'accept mission {0}'.format(url)
	return urllib2.urlopen(url, timeout=timeout).read()

if __name__ == "__main__":
	if len(sys.argv) > 1:
		if sys.argv[1] == '1':
			pool = ThreadPool(3)
			ret = dict((pool.apply_async(load_url, (url, 60)), url) for url in URLS)
			pool.close()
			pool.join()
			for i in ret:
				print '%r page is %d bytes' % (ret[i], len(i.get()))
		if sys.argv[1] == '2':
			with futures.ThreadPoolExecutor(max_workers = 3) as executor:
				future_to_url = dict((executor.submit(load_url, url, 60), url) for url in URLS)
				for future in futures.as_completed(future_to_url):
					url = future_to_url[future]
					if future.exception() is not None:
						print '%r generated an exception: %s' % (url, future.exception())
					else:
						print '%r page is %d bytes' % (url, len(future.result()))
		if sys.argv[1] == '3':
			pool = multiprocessing.Pool(processes = 3)
			ret = dict((pool.apply_async(load_url, (url, 60)), url) for url in URLS)
			pool.close()
			pool.join()
			for i in ret:
				print '%r page is %d bytes' % (ret[i], len(i.get()))
	else:
		print '\n' + '*' * 40 + '\n'
		print 'execute: python ' + os.path.realpath(__file__) + ' (mod)\n'
		print 'mod:'
		print '    1:   multiprocessing.pool ThreadPool (ThreadPool)'
		print '    2:   futures.ThreadPoolExecutor (ThreadPool)'
		print '    3:   multiprocessing.Pool (ProcessPool)'
		print '\n' + '*' * 40 + '\n'

关键理解 #

这类笔记最重要的不是把命令背下来,而是弄清楚它解决的是什么问题、依赖什么环境、执行后会改变什么。以后再次遇到类似情况时,可以先根据标题判断问题方向,再对照原始命令确认是否适合当前系统版本。

如果命令中包含具体路径、网卡名、磁盘名、进程名、IP 地址、端口号、用户名称或软件版本,实际执行时都要替换成自己环境中的真实值。不要直接照抄示例里的占位内容。

验证方法 #

验证时可以先准备一个最小示例,把输入、输出和异常情况都跑一遍。对于脚本类工具,建议先在临时目录或测试文件上执行,确认结果正确后再处理真实数据。

如果验证结果和预期不一致,建议先不要继续叠加更多修改,而是回到第一步检查环境差异。很多问题并不是命令本身错误,而是当前系统版本、软件版本、路径名称或权限条件与原记录不一致。

注意事项 #

涉及文件批量处理、系统命令调用、进程池线程池或第三方包时,要特别注意异常处理和边界条件,避免脚本中途失败后留下半成品数据。

对于旧文章中的命令,还要考虑软件版本变化。浏览器 flags、Linux 发行版默认配置、Python 包版本、Windows 系统设置都会随着时间调整。再次使用时,最好把这篇记录当成排查思路,而不是绝对固定的唯一答案。

小结 #

这篇记录可以作为一个快速索引:先看标题确认问题类型,再看原始命令找到核心操作,最后结合验证方法确认是否真正生效。这样既保留了早期备忘的简洁性,也能减少以后重复排查的时间。