array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Python并发编程 - 爱码网
laohanshuibi

     正确合理地使用并发编程,无疑会给我们的程序带来极大的性能提升。今天我就带大家一起来剖析一下python的并发编程。这进入并发编程之前,我们首先需要先了解一下并发和并行的区别。

首先你需要知道,并发并不是指同一时刻有多个操作同时进行。相反,某个特定的时刻,它只允许有一个操作发生,只不过线程或任务之间会互相切换,直到完成。如下图所示:

 

     图中出现了线程(thread) 和任务(task) 分别对应Python中两种并发形式--多线程(threading)和协程(asyncio)。对于多线程来说,是由操作系统来控制线程切换的。而对于 asyncio来说,主程序想要切换任务时,必须得到此任务可以被切换的通知。

     对于并行来说,是指同一时刻、同时执行任务。如下图所示:

 

 

     python中的多进程(multi-processing)是Python中的并行的实现形式。

     对比来看,并发通常应用于I/O操作频繁的场景,而并行通常应用于CPU负载重的场景。

 

单线程与多线程性能比较

 

    下面我们来比较一下单线程和多线程的性能区别。

    我们先看一下单线程版本。

import time

def process(work):
    time.sleep(2)
    print('process {}'.format(work))


def process_works(works):
    for work in works:
        process(work)

def main():
    works = [
        'work1',
        'work2',
        'work3',
        'work4'
    ]
    start_time = time.time()
    process_works(works)
    end_time = time.time()
    print('use {} seconds'.format(end_time - start_time))


if __name__ == '__main__':
    main()

##输出##
process work1
process work2
process work3
process work4
use 8.016737222671509 seconds

  

    单线程是最简单也是最直接的。

  • 先是遍历任务列表;
  • 然后对当前任务进行操作;
  • 等到当前操作完成后,再对下一个任务进行同样的操作,一直到结束。

    我们可以看到总共耗时约 8s。单线程的优点是简单明了,但是明显效率低下,因为上述程序的绝大多数时间,都浪费在了 I/O 等待上(假设time.sleep(2)是处理IO的时间)。下面我们来看一下多线程实现的版本。

import time
import concurrent.futures

def process(work):
    time.sleep(2)
    print('process {} '.format(work))


def process_works(works):
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        executor.map(process, works)

def main():
    works = [
        'work1',
        'work2',
        'work3',
        'work4'
    ]
    start_time = time.time()
    process_works(works)
    end_time = time.time()
    print('use {} seconds'.format(end_time - start_time))


if __name__ == '__main__':
    main()


####输出####
process work1 
process work2 
process work3 
process work4 

use 2.006268262863159 seconds

  

可以看到耗时用了2s多,一下子效率提升了4倍。我们来分析一下下面这段代码。
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
         executor.map(process, works)

  

   这里我们创建了一个线程池,总共有4个线程可以分配使用。excuter.map()表示对 works 中的每一个元素,并发地调用函数 process()。

 

并发编程之Asyncio

 

    下面我们在来学习一下并发编程的另一种实现形式--Asyncio。Asyncio是单线程的,它只有一个主线程,但是可以运行多个不同的任务(task),这些不同的任务,被一个叫做 event loop 的对象所控制。你可以把这里的任务,类比成多线程版本里的线程。

    为了简化讲解这个问题,我们可以假设任务只有两个状态:一是预备状态;二是等待状态。所谓的预备状态,是指任务目前空闲,但随时待命准备运行。而等待状态,是指任务已经运行,但正在等待外部的操作完成,比如 I/O 操作。在这种情况下,event loop 会维护两个任务列表,分别对应这两种状态;并且选取预备状态的一个任务,使其运行,一直到这个任务把控制权交还给 event loop 为止。当任务把控制权交还给 event loop 时,event loop会根据其是否完成,把任务放到预备或等待状态的列表,然后遍历等待状态列表的任务,查看他们是否完成。如果完成,则将其放到预备状态的列表;如果未完成,则继续放在等待状态的列表。而原先在预备状态列表的任务位置仍旧不变,因为它们还未运行。这样,当所有任务被重新放置在合适的列表后,新一轮的循环又开始了:event loop 继续从预备状态的列表中选取一个任务使其执行…如此周而复始,直到所有任务完成。

     接下来我们看一下如何通过Asyncio来实现并发编程。

import asyncio
import time


async def process(work):
    await asyncio.sleep(2)
    print('process {}'.format(work))



async def process_works(works):
    tasks = [asyncio.create_task(process(work)) for work in works]
    await asyncio.gather(*tasks)


def main():
    works = [
                'work1',
                'work2',
                'work3',
                'work4'
            ]
    start_time = time.time()
    asyncio.run(process_works(works))
    end_time = time.time()
    print('use {} seconds'.format(end_time - start_time))


if __name__ == '__main__':
    main()
    
####输出####
process work1
process work2
process work3
process work4
use 2.0058629512786865 seconds

  

    到此为止,我们已经把python的两种并发编程方式多线程和Asyncio都讲完了。不过,遇到实际问题时,我们该如何进行选择呢?总的来说我们应该遵循以下规范。

  • 如何I/O负载高,并且I/O操作很慢,需要很多任务/线程协同实现,那么使用 Asyncio 更合适。
  • 如何I/O负载高,并且I/O操作很快,只需要有限数量的任务/线程,那么使用多线程就可以了。

欢迎大家留言和我交流。

相关文章: