python使用多进程或者多线程
来源:互联网 发布:mac root mima 编辑:程序博客网 时间:2024/06/05 04:47
使用python做计算的时候,为了加快速率,可以启用多进程或者多线程。那几时使用多进程或者多线程呢
如果是io型,使用多线程,如果是cpu型,使用多进程。
理论上说,使用多进程是效率最大的,而且io型也可以使用,但是会使用更多资源,所以有时候在没必要使用多进程时,就用多线程。在io型的时候,由于主要用于等待,使用多进程就没必要了。
我用的是多进程multipleprocess。记住一个小点,使用多进程容易cpu飙升到90+,影响其他应用的使用,可适当的sleep。
Python Multithreading Tutorial: Concurrency and Parallelism
简要翻译上面文章。我当时是参考这篇的。
经常谈论说很难使用Python的多线程工作,指责所谓的全局解释器锁(GIL),他可以防止多个线程同时运行Python代码。因此,线程模块的使用不像我们使用C或java那样。必须明确的一点是,人们仍然可以用Python编写代码使其运行或并行,来大大提升效率,只要某些细节做好。
在Python并发的教程,我们要写一个python脚本来自Imgur下载最流行的图像。我们将开始一个版本,下载图片按照顺序,或只下载一次。作为先决条件,你必须登记在有一个应用程序
本教程中的脚本用Python 3.4.2(笔者也是用这个版本)
开始使用多线程Python
让我们开始创建一个Python模块,命名为“下载。py。该文件将包含所有必要的功能来获取图像列表和下载。我们将这些功能分成三个独立的功能:
- get_links
- download_link
- 下载安装程序目录
import jsonimport loggingimport osfrom pathlib import Pathfrom urllib.request import urlopen, Requestlogger = logging.getLogger(__name__)def get_links(client_id): headers = {'Authorization': 'Client-ID {}'.format(client_id)} req = Request('https://api.imgur.com/3/gallery/', headers=headers, method='GET') with urlopen(req) as resp: data = json.loads(resp.readall().decode('utf-8')) return map(lambda item: item['link'], data['data'])def download_link(directory, link): logger.info('Downloading %s', link) download_path = directory / os.path.basename(link) with urlopen(link) as image, download_path.open('wb') as f: f.write(image.readall())def setup_download_dir(): download_dir = Path('images') if not download_dir.exists(): download_dir.mkdir() return download_dir
接下来,我们需要写一个模块,可以使用这些功能来下载图像,一个一个的。我们将这名“single.py”。这将包含我们的第一个主要功能,图像下载。该模块将通过环境变量“imgur_client_id”获取clientId。它将调用“setup_download_dir”创建目标目录的下载。最后,它会获取列表中使用get_links函数图像,过滤掉所有的GIF和专辑的网址,然后使用“download_link”下载并保存这些图像到磁盘。这里是“single.py”
import loggingimport osfrom time import timefrom download import setup_download_dir, get_links, download_linklogging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')logging.getLogger('requests').setLevel(logging.CRITICAL)logger = logging.getLogger(__name__)def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] for link in links: download_link(download_dir, link) print('Took {}s'.format(time() - ts))if __name__ == '__main__': main()
下面通过并发,我们可以节省很多时间。
使用多线程
Threading是最通用的方法。线程更轻量。线程比进程,他共享相同的内存空间。
我们将写一个新的模块来代替“single.py”。该模块将创建一个线程池8,一共有9个线程,包括主线程。我选择了8个工作线程,因为我的电脑有8个CPU核心,每个核心一个工作者线程似乎是一个好的号码,立刻运行多线程。在实践中,这一数字更仔细的选择,要考虑其他因素,因为还有其他应用程序和服务运行在同一台机器。
from queue import Queuefrom threading import Threadclass DownloadWorker(Thread): def __init__(self, queue): Thread.__init__(self) self.queue = queue def run(self): while True: # Get the work from the queue and expand the tuple directory, link = self.queue.get() download_link(directory, link) self.queue.task_done()def main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] # Create a queue to communicate with the worker threads queue = Queue() # Create 8 worker threads for x in range(8): worker = DownloadWorker(queue) # Setting daemon to True will let the main thread exit even though the workers are blocking worker.daemon = True worker.start() # Put the tasks into the queue as a tuple for link in links: logger.info('Queueing {}'.format(link)) queue.put((download_dir, link)) # Causes the main thread to wait for the queue to finish processing all the tasks queue.join() print('Took {}'.format(time() - ts))
运行这个脚本在同一台机器耗时4.1s,比上面的结果快4.7倍。虽然这是更快,更值得一提的是,只有一个线程在执行一次在整个过程中,由于gil。因此,本代码是concurrent but not parallel(“并行但不并发”,我不知道这样翻译是否合适)。原因是更快是因为这是一个IO绑定的任务。该处理器同时下载这些图像,实际上大部分的时间花在等待网络。这就是为什么线程可以提供一个大的速度增加。该处理器可以切换线程,其中一个是准备做一些工作。使用线程模块Python或任何其他解释语言,只要有GIL,实际上可以导致性能降低。如果你的代码执行的是CPU绑定的任务,如解压缩gzip文件,使用线程模块会导致较慢的执行时间。而对于CPU绑定的任务和真正的并行执行,我们可以使用多处理模块。
Multiple Processe
多进程更容易操作。
使用多个进程创建一个pool。随着map的方法提供,我们把urllist传进到pool中的,这将产生8个新进程,并行下载。这是真正的并行性,但这是有代价的。每个子进程都使用着该script所创建的资源。在这个简单的例子,它不是一个大问题,但它很容易成为严重的开销。(其实相当于你跑了多个script的实例,每个实例都拥有自己的内存,译者自己使用的时候,cpu经常飙升,所以要注意优化和sleep)
from functools import partialfrom multiprocessing.pool import Pooldef main(): ts = time() client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] download = partial(download_link, download_dir) with Pool(8) as p: p.map(download, links) print('Took {}s'.format(time() - ts))
Distributing to Multiple Workers
当线程和多处理模块是cool的,但是只能在同一台机器,如果是分布式呢?如果你有长时间运行的任务,你不想在同一台机器用太多线程或者进程,这会降低应用程序的性能。
这便是Python库RQ,一个非常简单但功能强大的lib。把参数传进去。添加到redis列表入队的工作是第一步,但不会做任何事情。我们还需要至少一个worker监听作业队列。
第一步是安装和运行在你的计算机一个redis服务器,或访问一个运行redis服务器。在那之后,只有一些小的变化,对现有的代码。我们首先创建一个RQ队列实例,并把它传给redis server。然后嗲用我们称之为“q.enqueue(download_link,download_dir,link)”。Enqueue方法接受一个函数作为第一个参数,然后任何其他参数或关键字参数是传递给该函数的。
最后一步我们需要做的就是启动一些工人。rq提供了一个方便的脚本在默认的队列运行。运行“rqworker”在一个终端窗口就将开始一个工人listen的默认队列。请确保您的当前工作目录是脚本存放的目录。如果你想要听一个不同的队列,您可以运行“rqworker queue_name”它会听那个队列。关于RQ还有很方便,只要你能够连接使用redis,您可以在多台机器运行,来进行监听处理。因此,它是扩大规模好工具。这里是RQ版本源码:
from redis import Redisfrom rq import Queuedef main(): client_id = os.getenv('IMGUR_CLIENT_ID') if not client_id: raise Exception("Couldn't find IMGUR_CLIENT_ID environment variable!") download_dir = setup_download_dir() links = [l for l in get_links(client_id) if l.endswith('.jpg')] q = Queue(connection=Redis(host='localhost', port=6379)) for link in links: q.enqueue(download_link, download_dir, link)
总结
如果你的代码是IO绑定,多线程和多线程Python都可以。多进程比较容易,但具有更高的内存开销。如果你的代码被CPU绑定的,多进程会是更好的选择,尤其是如果目标机有多核CPU。对于Web应用程序,当你需要工作在多台机器上,RQ更好。
- python使用多进程或者多线程
- python 多线程多进程
- python多线程、多进程
- python 多线程/多进程
- Python 多进程 多线程
- python多进程多线程
- python 多进程,多线程
- linux下多进程或者多线程
- GDB 调试多进程或者多线程应用
- 【多进程与多线程】为什么在Python里推荐使用多进程而不是多线程?
- python多线程与多进程 超简单使用
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- 为什么在Python里推荐使用多进程而不是多线程?
- linux上的文件管理命令
- Android开发基础规范(一)
- 使用泛型参数传递对数据库进行增删改查
- 欢迎使用CSDN-markdown编辑器
- CUDA ---- Stream and Event
- python使用多进程或者多线程
- 电路设计_为什么晶振的频率是32.768kHz?
- 【OpenGL】理解glClear函数
- Leetcode 13. Roman to Integer & 12. Integer to Roman
- BlueMix cf push error
- Socket编程基本概念----imooc(2)
- Dubbo架构设计详解
- 工具类与工具函数(一)—— NextPrime
- 【58.75%】【BZOJ 1087】[SCOI2005]互不侵犯King