精华内容
下载资源
问答
  • 线程、进程、多线程多进程 多任务 小结

    千次阅读 多人点赞 2019-04-20 11:59:56
    3 多进程 4 多线程 5 线程与进程的关系 6 线程进程的区别 7 进程的优缺点 7.1 进程的优点 7.2 进程的缺点 8 线程的优缺点 8.1 线程的优点 8.2 线程的缺点 9 多线程的优缺点 9.1 多线程的优点 9.2 ...

    目录

    1 进程

    2 线程

    3 多进程

    4 多线程

    5 线程与进程的关系

    6 线程和进程的区别

    7 进程的优缺点

    7.1 进程的优点

    7.2 进程的缺点

    8 线程的优缺点

    8.1 线程的优点

    8.2 线程的缺点

    9 多线程的优缺点

    9.1 多线程的优点

    9.2 多线程的缺点

    10多进程的优缺点

    10.1 多进程的优点

    10.2 多进程的缺点

    8 多任务(多进程)

    参考资料


    最近经常看到 多进程,多线程和多任务等名词,很容易混。网上查了很多资料,内容很多。作为Linux初学者,还是想从最基础的开始了解,找通俗的例子了解,由浅入深。我把网上查阅的资料整理的一下,一次性全部摸透,还是有点难度的。写个博客,记录一下,以便后期查阅复习。

    首先,从定义开始,先看一下教科书上 进程线程定义:

    进程:资源分配的最小单位。

    线程:程序执行的最小单位。

    心中默念,啥啥啥,写的这是啥。于是乎 我就想到王宝强一脸懵逼的表情。

     

    1 进程

    进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集。从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。

    举例说明进程

    想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕。他有做生日蛋糕的食谱,厨房里有所需的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序(即用适当形式描述的算法)计算机科学家就是处理器(CPU),而做蛋糕的各种原料就是输入数据。进程就是厨师阅读食谱、取来各种原料以及烘制蛋糕等一系列动作的总和。现在假设计算机科学家的儿子哭着跑了进来,说他的头被一只蜜蜂蛰了。计算机科学家就记录下他照着食谱做到哪儿了(保存进程的当前状态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这里,我们看到处理机制是从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程拥有各自的程序(食谱和急救手册)。当蜜蜂蛰伤处理完之后,这位计算机科学家又回来做蛋糕,从他离开时的那一步继续做下去。

     

    2 线程

    线程是CPU调度的最小单位(程序执行流的最小单元),它被包含在进程之中,是进程中的实际运作单元。一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    一个标准的线程有线程ID、当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单元,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现处间断性。

    线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

     

    举例说明线程

    假设,一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。若只有一个进程,势必造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。若有多个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协作涉及到了进程通信问题,而且有共同都需要拥有的东西——-文本内容,不停的切换造成性能上的损失。若有一种机制,可以使任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带来的性能损耗,那就好了。这种机制就是线程

    总的来说进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。

     

     

    3 多进程

    进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的(静态的),进程是活的(动态的)。进程可以分为系统进程用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;所有由用户启动的进程都是用户进程。进程是操作系统进行资源分配的单位。 进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。 多任务带来的好处是明显的,比如你可以边听网易云音乐,一边上网,与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。那么这里就涉及到并行的问题,俗话说,一心不能二用,这对计算机也一样,原则上一个CPU只能分配给一个进程,以便运行这个进程。我们通常使用的计算机中只有一个CPU,也就是说只有一颗心,要让它一心多用,同时运行多个进程,就必须使用并发技术。实现并发技术相当复杂,最容易理解的是“时间片轮转进程调度算法”,它的思想简单介绍如下:在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来 CPU是在轮流为多个进程服务,就好像所有的进程都在不间断地运行一样。但实际上在任何一个时间内有且仅有一个进程占有CPU。 如果一台计算机有多个CPU,情况就不同了,如果进程数小于CPU数,则不同的进程可以分配给不同的CPU来运行,这样,多个进程就是真正同时运行的,这便是并行

    并行处理(Parallel Processing)是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面。并行处理的主要目的是节省大型和复杂问题的解决时间。并发处理(concurrency Processing):指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机(CPU)上运行

    并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说,并行是并发的子集。

     

     

    4 多线程

    线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单元。在单一程序中同时运行多个想成完成不同的工作,称为多线程

    多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率。线程是在同一时间需要完成多项任务的时候被实现的。

    打个比方:

    多进程是立体交通系统(近似于立交桥),虽然造价高,上坡下坡多耗点油,但是不堵车。

    多线程是平面交通系统,造价低,但红绿灯太多,老堵车。

     

     

    5 线程与进程的关系

    (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;

    (2)资源分配给进程,同一进程内的所有线程共享该进程的所有资源;

    (3)线程在执行过程中需要协作同步。不同进程中的线程之间要利用消息通信的方法实现同步;

    (4)处理机分配给线程,即真正在处理机上运行的是线程;

    (5)线程是进程的一个执行单元,也是进程内的可调用实体。

     

     

    6 线程和进程的区别

    (1)线程共享内存空间;进程的内存是独立的;

    (2)同一个进程的线程之间可以直接交流;两个进程想通信,必须通过一个中间代理来实现;

    (3)创建新进程很简单;创建新进程需要对其父进程进行一个克隆;

    (4)一个线程可以控制和操作同一进程里的其他线程;但是进程只能操作子进程;

    (5)改变注线程(如优先权),可能会影响其他线程;改变父进程,不影响子进程。

    (6)调度:线程作为分配和调度的基本单位,进程作为拥有资源的基本单位

    (7)并发性:不进进程之间可以并发执行,同一进程内的线程也可以并发执行

    (8)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的系统资源

    (9)系统开销:在创建和撤销进程的时候,系统都要分配和回收资源,导致系统的明显大于创建和撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式的下不会对其他进程造成影响,而线程只是进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有独立的地址空间,一个线程死后就等于整个进程死掉,所以多进程程序要比多线程程序健壮,但是在进程切换的时候消耗的资源较大,效率差。

    根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。

    总结:多线程执行效率高;  多进程耗资源,安全。

     

     

    7 进程的优缺点

    7.1 进程的优点

    1)顺序程序的特点:具有封闭性和可再现性;

    2)程序的并发执行和资源共享。多道程序设计出现后,实现了程序的并发执行和资源共享,提高了系统的效率和系统的资源利用率。

     

    7.2 进程的缺点

    操作系统调度切换多个线程要比切换调度进程在速度上快的多。而且进程间内存无法共享,通讯也比较麻烦。线程之间由于共享进程内存空间,所以交换数据非常方便;在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。  

     

     

    8 线程的优缺点

    8.1 线程的优点

    1)它是一种非常"节俭"的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码 段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程 所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。当然,在具体的系统上,这个数据可能 会有较大的区别;

    2)线程间方便的通信机制,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便;

    3)使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上;

    4)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

     

    8.2 线程的缺点

    1)调度时, 要保存线程状态,频繁调度,需要占用大量的机时;

    2)程序设计上容易出错(线程同步问题)。

     

     

    9 多线程的优缺点

    9.1 多线程的优点

    1)无需跨进程边界; 程序逻辑和控制方式简单;

    2)所有线程可以直接共享内存和变量等;

    3)线程方式消耗的总资源比进程方式好。

     

    9.2 多线程的缺点

    1)每个线程与主程序共用地址空间,受限于2GB地址空间;

    2)线程之间的同步和加锁控制比较麻烦; 一个线程的崩溃可能影响到整个程序的稳定性;

    3)到达一定的线程数程度后,即使再增加CPU也无法提高性能,例如Windows Server 2003,大约是1500个左右的线程数就快到极限了(线程堆栈设定为1M),如果设定线程堆栈为2M,还达不到1500个线程总数;

    4)线程能够提高的总性能有限,而且线程多了之后,线程本身的调度也是一个麻烦事儿,需要消耗较多的CPU 。

     

    10多进程的优缺点

    10.1 多进程的优点

    1)每个进程互相独立,不影响主程序的稳定性,子进程崩溃没关系;

    2)通过增加CPU,就可以容易扩充性能;

    3)可以尽量减少线程加锁/解锁的影响,极大提高性能,就算是线程运行的模块算法效率低也没关系;

    4)每个子进程都有2GB地址空间和相关资源,总体能够达到的性能上限非常大。

     

    10.2 多进程的缺点

    1)逻辑控制复杂,需要和主程序交互;

    2)需要跨进程边界,如果有大数据量传送,就不太好,适合小数据量传送、密集运算 多进程调度开销比较大。

     

    总结:最好是多进程和多线程结合,即根据实际的需要,每个CPU开启一个子进程,这个子进程开启多线程可以为若干同类型的数据进行处理。当然你也可以利用多线程+CPU+轮询方式来解决问题……方法和手段是多样的,关键是自己看起来实现方便有能够满足要求,代价也合适。

     

    按照多个不同的维度(类别),来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受)。

        对比类别

                                        多进程

                                     多线程

         总结

    数据共享、同步

    数据共享复杂,需要用IPC;数据是分开的,同步简单

    因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂

     各有优势

        内存、CPU

    占用内存多,切换复杂,CPU利用率低

    占用内存少,切换简单,CPU利用率高

     线程占优

     创建销毁、切换

    创建销毁、切换复杂,速度慢

    创建销毁、切换简单,速度很快

     线程占优

        编程、调试

    编程简单,调试简单

    编程复杂,调试复杂

     进程占优

           可靠性

    进程间不会互相影响

    一个线程挂掉将导致整个进程挂掉

     进程占优

           分布式

    适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单

    适应于多核分布式

     进程占优

    其实没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。

    1)需要频繁创建销毁的优先用线程

    这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的

     

    2)需要进行大量计算的优先使用线程

    所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。这种原则最常见的是图像处理、算法处理。

     

    3)强相关的处理用线程,弱相关的处理用进程

    什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。

    一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。

     

    4)可能要扩展到多机分布的用进程,多核分布的用线程

     

    5)都满足需求的情况下,用你最熟悉、最拿手的方式

    至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。需要提醒的是:虽然我给了这么多的选择原则,但实际应用中基本上都是“进程+线程”的结合方式,千万不要真的陷入一种非此即彼的误区。

     

    8 多任务(多进程)

    现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。

    什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word写论文,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

    现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢

    其实操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。

    真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

    对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

    有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些 “子任务” 称为线程(Thread)。

    由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。

     

     

    参考资料

    [1] https://www.cnblogs.com/pingqiang/p/8007549.html#多进程多线程

    [2] https://www.cnblogs.com/ww36315610/p/3881533.html?tdsourcetag=s_pctim_aiomsg

    [3] http://www.cnblogs.com/Yogurshine/p/3640206.html

    [4] https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868322563729e03f6905ea94f0195528e3647887415000?tdsourcetag=s_pctim_aiomsg

    [5] https://www.cnblogs.com/yuanchenqi/articles/6755717.html#_label3?tdsourcetag=s_pctim_aiomsg

    [6] https://www.cnblogs.com/zhanht/p/5401685.html?tdsourcetag=s_pctim_aiomsg

    [7] https://www.kafan.cn/edu/85866456.html

     

    展开全文
  • 多进程和多线程的概述   1:多进程和多线程的概述 要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为 线程是依赖于进程而存在的。 2:什么是进程?  通过任务管理器我们就看到了进程的存在...

    多进程和多线程的概述

     

    1:多进程和多线程的概述

    要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为 线程是依赖于进程而存在的


    2:什么是进程?
        通过任务管理器我们就看到了进程的存在。
        而通过观察,我们发现只有运行的程序才会出现进程。
        进程:正在运行的程序。
        进程 是操作系统进行资源分配和调度的独立单位。每一个进程都有它自己的内存空间和系统资源。
        
    3:多进程有什么意义呢?
        单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
        举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。则游戏进程和音乐进程是同时进行的。
        也就是说,现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务,可以提高CPU的使用率。
        
        问题:
            一边玩游戏,一边听音乐是同时进行的吗?
            不是。因为 单CPU在某一个时间点上 只能做一件事情。
            而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让我们觉得是同时进行的。
            
    4:什么是线程呢?
        在同一个进程内又可以执行多个任务,而这每一个任务我们就可以看作是一个线程。
        线程:是进程中的单个顺序控制流,是一条执行路径。
        线程:是进程的执行单元(执行单元也可以叫做执行路径),是进程使用CPU的最基本单位。
        单线程:如果程序只有一条执行路径。
        多线程:如果程序有多条执行路径。

        
    5:多线程有什么意义呢?
        多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
        程序的执行其实都是在抢CPU的资源,或者说是抢CPU的执行权。
        多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
        我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。

          
    而多线程却给了我们一个错觉:让我们认为多个线程是并发执行的。其实不是。
    因为多个线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立的,一个线程一个栈。所以,它们仍然是在抢CPU的资源执行。一个时间点上只能有一个线程执行,而且谁抢到,这个不一定,所以,造成了线程运行的随机性。
        
    6:那么什么又是并发呢?
        大家注意两个词汇的区别:并行和并发
        并行 是逻辑上同时发生,指在某一个时间段内同时运行多个程序。
        并发 是物理上同时发生,指在某一个时间点同时运行多个程序。(如何解决高并发的问题?需要大数据的知识!)
        那么,我们能不能实现真正意义上的并发呢,可以,多个CPU就可以实现,不过你得知道如何调度和控制它们。

     

    注:该系列博客内容参考自传智播客刘意老师视频。

     

     

    展开全文
  • 多进程,多线程,协程实现简单举例

    千次阅读 2019-03-21 23:18:06
    文章目录多进程多线程的对比1. 多任务的实现原理2. 多进程3. 多线程4. 计算密集型 与 IO密集型进程1. 一个进程的举例:2. 启动多个进程实现多任务:3. 使用 .join() 等待子进程结束后再执行父进程4. 全局变量在多个...

    多进程与多线程的对比

    1. 多任务的实现原理

    一般地, 设计Master-Worker模式, Master负责分配任务, Worker负责执行任务, 所以在多任务环境下, 一般是由一个Master, 多个Worker.

    2. 多进程

    主进程就是Master, 其他进程就是Worker

    • 优点:
      稳定性好:
      一个子进程崩溃了, 不会影响主进程和其他子进程. 当然的, 主进程挂了后, 所有进程就全挂了, 但是Master进程只负责分配任务, 挂掉的概率很低.

    • 缺点:

      • 创建进程的代价大:
        在Unix/Linux系统下, 用fork调用还行, 在windows下创建进程开销巨大.

      • 操作系统能同时运行的进程数也是有限的:
        在内存和CPU的限制下, 如果有几千个进程同时运行, 操作系统连调度都成问题.

    3. 多线程

    主线程就是Master, 其他线程就是Worker

    • 优点:

      多线程模式模式通常比多进程快一点, 但是也快的不多

      在windows下, 多线程的效率比多进程要高

    • 缺点:

      任何一个线程挂掉都可能直接造成整个进程崩溃(所有线程共享进程的内存. 在windows上, 如果一个线程执行的代码除出了问题, 可以进程看到这样的提示:“该程序执行了非法操作, 即将关闭”, 其实往往是某个线程出了问题, 但是操作系统会强制结束整个进程)

    4. 计算密集型 与 IO密集型

    • 计算密集型: 适合多进程

      要进行大量的计算, 消耗CPU资源, 比如计算圆周率/对视频进行高清解码等, 全靠CPU的运算能力. 这种计算密集型任务虽然也可以用于多任务完成, 但是任务越多, 花在任务切换的时间也越多, CPU执行任务的效率就会越低. 因此, 要最高效地利用CPU, 计算密集型任务同时进行的数量应该等于CPU的核心数.

    • IO密集型: 适合多线程

      设计到网络/磁盘IO的任务都是IO密集型任务, 这类任务特点是CPU消耗很少, 任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度). 对于IO密集型任务, 任务越多, CPU效率越高, 但也有一个限度. 常见的大部分任务都是IO密集型任务, 比如Web任务.

    进程

    对于操作系统而言,一个任务就是一个进程.
    进程是系统中程序执行和资源分配的基本单位。每个进程都有自己的数据段、代码段、和堆栈段.

    1. 一个进程的举例:

    from time import sleep
    
    def run():
        while True:
            print("this is a  run function")
            sleep(1.2)
    
    if __name__ == "__main__":
        while True:
            print("this is mian function")
            sleep(1)
    
        # 不会执行到run方法,只有上面的while循环结束才可以执行
        run()
    

    2. 启动多个进程实现多任务:

    '''
    现代操作系统(Windows、Mac OS X、Linux、UNIX等)都支持“多任务”
    
    什么叫多任务???
    操作系统同时可以运行多个任务
    
    单核CPU实现多任务原理:操作系统轮流让各个任务交替执行,QQ执行2us,切换到微信,在执行2us,再切换到微博,执行2us……。表面是看,每个任务反复执行下去,但是CPU调度执行速度太快了,导致我们感觉就行所有任务都在同时执行一样
    
    多核CPU实现多任务原理:真正的秉性执行多任务只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作西永也会自动把很多任务轮流调度到每个核心上执行
    并发:看上去一起执行,任务书多于CPU核心数
    并行:真正一起执行,任务数小于等于CPU核心数
    
    实现多任务的方式:
    1、多进程模式
    2、多线程模式
    3、协程模式
    4、多进程+多线程模式
    '''
    
    '''
    multiprocessing 库
    跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象
    '''
    from multiprocessing import Process
    from time import sleep
    import os
    
    #子进程需要执行的买吗
    def run(str):
        while True:
            # os.getpid()获取当前进程id号
            # os.getppid()获取当前进程的父进程id号
            print("str %s, current process %s,  parrent process %s"%(str, os.getpid(), os.getppid()))
            sleep(1)
    
    if __name__ == "__main__":
        print("主(父)进程启动-%s"%(os.getpid()))
        #创建子进程
        #target说明进程执行的任务
        p = Process(target=run, args=("first",))
        #启动进程
        p.start()
        # sleep(1)
        while True:
            print("main process : while True")
            sleep(1)
            
    # 主(父)进程启动-21240
    # main process : while True
    # main process : while True
    # str first, current process 17920,  parrent process 21240
    # main process : while True
    # str first, current process 17920,  parrent process 21240
    # main process : while True
    # str first, current process 17920,  parrent process 21240
    # ...
    

    3. 使用 .join() 等待子进程结束后再执行父进程

    from multiprocessing import Process
    from time import sleep
    import os
    
    def run(string_):
        print("子进程启动")
        sleep(string_)
        print("子进程结束")
    
    if __name__ == "__main__":
        print("父进程启动")
    
        p = Process(target=run, args=("nice",))
        p.start()
    
        #父进程的结束不能影响子进程,让父进程等待子进程结束再执行父进程
        p.join()
        print("父进程结束")
    # 父进程启动
    # 子进程启动
    # nice
    # 子进程结束
    # 父进程结束
    
    # 如果不加 p.join() 的运行结果:
    # 父进程启动
    # 父进程结束
    # 子进程启动
    # nice
    # 子进程结束
    

    4. 全局变量在多个进程中不能被共享

    from multiprocessing import Process
    
    num = 100
    
    def run():
        print("子进程开始")
        global num # num = 100
        num += 1
        print(num)
        print("子进程结束,num={0}".format(num))
    
    if __name__ == "__main__":
        print("父进程开始")
    
        p = Process(target=run)
        p.start()
        p.join()
    
        # 在子进程中修改全局变量对父进程中的全局变量没有影响
        # 在创建子进程时对全局变量做了一个备份,父进程中的与子进程中的num是完全不同的两个变量
        print("父进程结束,num=%d"%num)
        
    # 父进程开始
    # 子进程开始
    # 101
    # 子进程结束,num=101
    # 父进程结束,num=100
    

    5. 使用进程池 启动多个子进程

    from multiprocessing import Pool
    import os, time, random
    
    def run(name):
        print("子进程%d启动--%s" % (name, os.getpid()))
        start = time.time()
        time.sleep(random.choice([1,2,3]))
        end = time.time()
        print("子进程%d结束--%s--耗时%.2f" % (name, os.getpid(), end-start))
    
    if __name__ == "__main__":
        print("父进程启动")
        # 使用进程池 Pool: 创建多个进程
        # 表示可以同时执行的进程数量
        # Pool默认大小是CPU核心数
        # pp = Pool(1)        # 一个进程启动,结束后再启动下一个
        pp = Pool(2)   				# 两个进程启动,结束后再启动下两个
        for i in range(6):
            #创建进程,放入进程池统一管理
            pp.apply_async(run,args=(i,))
        # pp.apply_async(run_new)  # 也可以增加其他进程
    
        #在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
        pp.close()
        #进程池对象调用join,会等待进程池中所有的子进程结束完毕再去执行父进程
        pp.join()
    
        print("父进程结束")
    

    举例: 多进程来拷贝文件

    import os, time
    from multiprocessing import Pool
    # 实现文件的拷贝
    def copyFile(rPath, wPath):
        fr = open(rPath, "rb")
        fw = open(wPath, "wb")
        context = fr.read()
        fw.write(context)
        fr.close()
        fw.close()
    
    path = r"D:\file"
    toPath =r"D:\toFile"
    
    if __name__ == "__main__":
        # 读取path下的都有的文件
        filesList = os.listdir(path)
    
        start = time.time()
        pp = Pool(4)
        for fileName in filesList:
            pp.apply_async(copyFile, args=(os.path.join(path,fileName), os.path.join(toPath,fileName)))
    
        pp.close()
        pp.join()
        end = time.time()
        print("总耗时:%0.2f" % (end-start))
    

    6. 简单的进程间通信_Queue

    from multiprocessing import Process, Queue
    import os, time
    
    def write(q):
        print("启动写子进程%s" % (os.getpid()))
        for chr in ["A", "B", "C", "D"]:
            q.put(chr)
            time.sleep(1)
        print("结束写子进程%s" % (os.getpid()))
    
    def read(q):
        print("启动读子进程%s" % (os.getpid()))
        while True:
            value = q.get(True)
            print("value = " + value)
        print("结束读子进程%s" % (os.getpid()))  # 执行不到该行
    
    if __name__ == "__main__":
        #父进程创建队列,并传递给子进程
        q = Queue()
        pw = Process(target=write, args=(q,))
        pr = Process(target=read, args=(q,))
    
        pw.start()
        pr.start()
    
        pw.join()
        #pr进程里是个死循环,无法等待其结束,只能强行结束
        pr.terminate()
    
        print("父进程结束")
    

    7. 封装进程对象

    from multiprocessing import Process
    import os, time
    
    class MyProcess(Process):      # 继承 Process 类
        def __init__(self,name):
            Process.__init__(self)
            self.name = name
    
        def run(self):
            print("子进程(%s-%s)启动" % (self.name, os.getpid()))
            #子进程的功能
            time.sleep(3)
            print("子进程(%s-%s)结束" % (self.name, os.getpid()))
            
    myProcess = MyProcess("zyp")
    myProcess.run()
    # 子进程(zyp-13804)启动
    # 子进程(zyp-13804)结束      
    

    线程

    1. 线程

    '''
    在一个进程的内部,要同时干多件事,就需要同时运行多个“子任务”,
    我们把进程内的这些“子任务”叫做线程
    
    线程通常叫做轻型的进程。线程是共享内存空间的并发执行的多任务,
    每一个线程都共享一个进程的资源,共享一个堆栈.
    
    线程是最小的执行单元,而进程由至少一个线程组成。
    如何调度进程和线程,完全由操作系统绝对,程序自己不能决定什么时候执行,执行多长时间
    
    模块
    1、_thread模块       低级模块
    2、threading模块     高级模块,对_thread进行了封装
    '''
    

    2. 启动一个线程

    import threading, time
    
    def run(num):
        print("子线程(%s)开始" % (threading.current_thread().name))
        # 实现线程的功能
        time.sleep(2)
        print("num:", num)
        time.sleep(2)
        print("子线程(%s)结束" % (threading.current_thread().name))
    
    if __name__ == "__main__":
        # 任何进程默认就会启动一个线程,称为主线程,主线程可以启动新的子线程
        # current_thread():返回返回当前线程的实例
        print("主线程(%s)启动" % (threading.current_thread().name))
    
        # 创建子线程                     线程的名称
        t = threading.Thread(target=run, name="runThread", args=(1,))
        t.start()
    
        # 等待线程结束
        t.join()
    
        print("主线程(%s)结束" % (threading.current_thread().name))
    

    3. 线程间的共享数据

    多线程和多进程最大的不同在于,
    多进程中,同一个变量,各自有一份拷贝存在每个进程中,互不影响。
    多线程中,所有变量都由所有线程共享。所以,任何一个变量都可以被任意一个线程修改,
    因此,线程之间共享数据最大的危险在于多个线程同时修改一个变量,容易把内容改乱了。

    import threading
    
    num = 0
    
    def run(n):
        global num
        for i in range(1000000):
            num = num + n    #  15 = 9 + 6
            num = num - n    #  9
    
    if __name__ == "__main__":
        iTimeStart = time.time()
        t1 = threading.Thread(target=run, args=(6,))
        t2 = threading.Thread(target=run, args=(9,))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        print("num =",num)
        iTimeEnd = time.time()
        print(iTimeEnd - iTimeStart)   # 1.1113207340240479
    

    4. 线程锁解决数据混乱

    import threading
    import time
    
    # 锁对象
    lock = threading.Lock()
    
    num = 0
    def run(n):
        global num
    
        for i in range(1000000):
            # 锁 Lock 确保了这段代码只能由一个线程从头到尾的完整执行
            # 阻止了多线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,所以效率大大滴降低了
            # 由于可以存在多个锁,不同线程持有不同的锁,并试图获取其他的锁,可能造成死锁,导致多个线程挂起。只能靠操作系统强制终止
            '''
            lock.acquire()
            try:
                num = num + n    
                num = num - n   
            finally:
                #修改完一定要释放锁
                lock.release()
            '''
            #与上面代码功能相同,with lock可以自动上锁与解锁
            with lock:
                num = num + n
                num = num - n
    
    if __name__ == "__main__":
        iTimeStart = time.time()
        t1 = threading.Thread(target=run, args=(6,))
        t2 = threading.Thread(target=run, args=(9,))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        print("num =",num)
        iTimeEnd = time.time()
        print(iTimeEnd - iTimeStart)     # 6.983535051345825
    # 通过对比时间, 发现加锁后运行所需要的时间是 不加锁的近乎5倍以上!
    

    5. 创建全局的ThreadLocal 对象

    创建一个全局的ThreadLocal对象
    每个线程有独立的存储空间
    每个线程对ThreadLocal对象都可以读写,但是互不影响

    import threading
    
    # 创建全局ThreadLocal对象:
    local = threading.local()
    
    def process_student():
        # 获取当前线程关联的student:
        print('local.student: %s , current_thread : %s' % (local.student, threading.current_thread().name))
    
    def process_thread(stu_name):
        # 绑定ThreadLocal的student:
        local.student = stu_name
        process_student()
    
    t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
    t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    # local.student: Alice , current_thread : Thread-A
    # local.student: Bob , current_thread : Thread-B
    
    '''
    全局变量local就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。
    你可以把local看成全局变量,但每个属性如local.student都是线程的局部变量,
    可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。
    可以理解为全局变量local是一个dict,不但可以用local.student,还可以绑定其他变量,
    如local.teacher等等。
    应用:ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,
    这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
    '''
    

    小结
    一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。
    ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。

    import threading, time
    
    local = threading.local()  # 创建一个全局的ThreadLocal对象
    
    num = 0  # 将线程中需要访问的变量绑定到全局ThreadLocal对象上
    
    def run(x, n):
        x = x + n
        x = x - n
        return  x
    
    def func(n):
        #每个线程都有local.x,就是线程的局部变量
        local.x = num							 # 在线程调用的函数中, 将访问的变量和ThreadLock绑定
        for i in range(1000000):
            run(local.x, n)
        print("%s-  local.x =%d"%(threading.current_thread().name, local.x))
    
    if __name__ == "__main__":
        iTimeStart = time.time()
        t1 = threading.Thread(target=func, args=(6,))
        t2 = threading.Thread(target=func, args=(9,))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
        print("num =",num)
        iTimeEnd = time.time()
        print(iTimeEnd - iTimeStart)   # 1.6630573272705078
    # 不仅不会导致数据混乱, 而且所用时间已经接近不加锁的时间.
    

    6. 定时线程

    import threading, time
    
    def run():
        print("sunck is a good man")
    
    #延时执行线程
    t = threading.Timer(3, run)
    t.start()
    t1 = time.time()
    
    t.join()
    t2 =time.time()
    print("父线程结束")
    print(t2 - t1)
    

    7. 线程通信

    import threading, time
    
    def func():
        #事件对象
        event = threading.Event()
        def run():
            for i in range(5):
                #阻塞,等待事件的触发
                event.wait()
                #重置
                # event.clear()
                print("sunck is a good man!!%d"%i)
        t = threading.Thread(target=run).start()
        return event
    
    e = func()
    
    #触发事件
    for i in range(5):
        time.sleep(2)
        e.set()
    

    8. 生产者与消费者

    import threading,queue,time,random
    
    #生产者
    def product(id, q):
        while True:
            num = random.randint(0, 10000)
            q.put(num)
            print("生产者%d生产了%d数据放入了队列" % (id, num))
            time.sleep(3)
        #任务完成
        q.task_done()
    
    #消费者
    def customer(id, q):
        while True:
            item = q.get()
            if item is None:
                break
            print("消费者%d消费了%d数据" % (id, item))
            time.sleep(2)
        # 任务完成
        q.task_done()
    
    if __name__ == "__main__":
        # 消息队列
        q = queue.Queue()
    
        # 启动生产者
        for i in range(4):
            threading.Thread(target=product, args=(i,q)).start()
    
        # 启动消费者
        for i in range(3):
            threading.Thread(target=customer, args=(i,q)).start()
    

    协程

    子程序/函数:在所有语言中都是层级调用,比如A调用B,在B执行的过程中又可以调用C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。是通过栈实现的,一个线程就是执行一个子程序,子程序调用总是一个入口,一次返回,调用的顺序是明确的

    概述:看上去也是子程序,但执行过程中,在子程序的内部可中断,然后转而执行别的子程序,不是函数调用。

    1. 协程举例

    # Python对协程的支持是通过generator实现的
    def run():
        print('start')
        print(1)
        yield 10
        print(2)
        yield 20
        print(3)
        yield 30
    
    # 协程的最简单风格,控制函数的阶段执行,节约线程或者进程的切换
    # 返回值是一个生成器
    m = run()
    next(m)   # 1
    next(m)		# 2
    next(m)		# 3
    # print(next(m))		# 1  10
    # print(next(m))		# 2  20
    # print(next(m))		# 3  30
    

    2. 数据传输

    def run():
        #空变量,存储的作用data始终为空
        data = ""
        print(0, data)
        r = yield data  # 接受除了第二个send来的数据( 第一个send 的是None 来启动)
    
        print(1, r, data)
        r = yield "aa"
    
        print(2, r, data)
        r = yield "bb"
    
        print(3, r, data)
        r = yield "cc"
    
    m = run()
    #启动m, 首先需要使用 send(None)
    m.send(None)
    print("-----------")
    print(1,m.send("a"))
    print("-----------")
    print(2,m.send("b"))
    print("-----------")
    print(3,m.send("c"))
    

    3. 生产者与消费者

    def product(c):
        c.send(None)
        for i in range(5):
            print("生产者产生数据%d"%i)
            r = c.send(str(i))
            print("消费者消费了数据%s"%r)
        c.close()
    def customer():
        data = ""
        while True:
            n = yield data
            if not n:
                return
            print("消费者消费了%s"%n)
            data = "200"
    c = customer()
    product(c)
    
    展开全文
  • 多进程和多线程

    2017-03-01 19:02:15
    概念,我们都烂熟于心了,那么是否可以究其本质的分析下,多进程和多线程的优缺点,以便在实际场景中进行合适的选择了?一、多进程1、概念在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行...

    “进程是程序在处理机上的一个执行过程,是资源分配的基本单位”;“线程是CPU调度的一个基本单位”。概念,我们都烂熟于心了,那么是否可以究其本质的分析下,多进程和多线程的优缺点,以便在实际场景中进行合适的选择了?

    一、多进程

    1、概念

    在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。

    2、优点

    1)编程相对容易,通常不需要考虑锁和同步资源的问题。
    2)更强的容错性:一个进程崩溃了不会影响其他进程。

    3、缺点

    1)创建销毁、切换复杂,速度慢。
    2)数据共享麻烦,通常要用到共享内存、信号量等IPC技术;
    3)占用cpu多

    二、多线程

    1、概念

    1.1 线程的基本概念、线程的基本状态及状态之间的关系?

    线程,有时称为轻量级进程,是CPU使用的基本单元;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数据段和其他操作系统资源(如打开文件和信号)。

    线程有四种状态:新生状态、可运行状态、被阻塞状态、死亡状态

    1.2 多线程有几种实现方法,都是什么?

    多线程的实现方式?
    1)继承 Thread 类
    2) 实现 Runnable 接口再 new Thread(YourRunnableOjbect)

    1.3 线程安全

    如果多线程的程序运行结果是可预期的,而且与单线程的程序运行结果一样,那么说明是“线程安全”的。

    1.4 多线程同步和互斥有几种实现方法,都是什么,什么情况下使用,举例说明?

    线程间的同步
    线程间的同步方法大体可分为两类:用户模式和内核模式。
    顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
    用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。

    多线程互斥和同步的区别
    1)线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
    2)线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步(下文统称为同步)。

    2、线程优点

    1)创建速度快
    2)共享数据方便:多线程间可以共享同一虚拟地址空间;
    3)上下文切换开销小,不用切换地址空间,不用更改寄存器,不用刷新TLB。
    4)占用cpu少

    3、线程缺点

    1)数据共享方便的同时,也带来了同步问题。
    2)一个线程挂掉将导致整个进程挂掉

    三、对比

    进程与线程的区别?
    1)进程是资源分配的基本单位,线程是cpu调度,或者说是程序执行的最小单位。在Mac、Windows NT等采用微内核结构的操作系统中,进程的功能发生了变化:它只是资源分配的单位,而不再是调度运行的单位。在微内核系统中,真正调度运行的基本单位是线程。因此,实现并发功能的单位是线程。

    2)进程有独立的地址空间,比如在linux下面启动一个新的进程,系统必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种非常昂贵的多任务工作方式。而运行一个进程中的线程,它们之间共享大部分数据,使用相同的地址空间,因此启动一个线程,切换一个线      程远比进程操作要快,花费也要小得多。当然,线程是拥有自己的局部变量和堆栈(注意不是堆)的,比如在windows中用_beginthreadex创建一个新进程就会在调用CreateThread的同时申请一个专属于线程的数据块(_tiddata)。

    3)线程之间的通信比较方便。统一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。

    4)多进程比多线程程序要健壮。一个线程死掉整个进程就死掉了,但是在保护模式下,一个进程死掉对另一个进程没有直接影响。

    对比维度 多进程 多线程 总结
    数据共享、同步 数据共享复杂,需要用IPC; 数据是分开的,同步简单,因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 各有优势
    内存、CPU 占用内存多,切换复杂,CPU利用率低 占用内存少,切换简单,CPU利用率高 线程占优
    创建销毁、切换 创建销毁、切换复杂,速度慢 创建销毁、切换简单,速度很快 线程占优
    编程、调试 编程简单,调试简单 编程复杂,调试复杂 进程占优
    可靠性 进程间不会互相影响 一个线程挂掉将导致整个进程挂掉 进程占优
    分布式 适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较简单 适应于多核分布式 进程占优

    三、合理选择多线程还是多进程

    1、需要频繁创建销毁的优先用线程
    2、需要进行大量计算的优先使用线程
    3、可能要扩展到多机分布的用进程,多核分布的用线程

    展开全文
  • 1.想要了解多线程,必须先了解线程,而想了解线程,必须了解进程,因为线程是依赖于进程而存储在的 2.什么是进程? 通过任务管理器我们就看见了进程的存在 而通过观察,我们发现只有运行的程序才会出现进程 进程...
  • 多线程和多进程 及其应用场景

    万次阅读 多人点赞 2018-08-24 12:35:14
    一. 两者区别 进程是分配资源的基本单位;... ②更强的容错性:比起多线程的一个好处是一个进程崩溃了不会影响其他进程。  ③有内核保证的隔离:数据错误隔离。 对于使用如C/C++这些语言编写...
  • 线程和进程是什么?举例说明 该文章转自一篇文章,形象的说明进程线程之间的关系: http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
  • Python与多线程多进程

    2019-01-18 12:04:07
    多线程与多进程多线程与多进程优劣比较Python与多线程多进程CPUIO密集型举例判断方法:多线程多进程 多线程与多进程优劣比较 Python与多线程多进程 CPUIO密集型 举例 ...CPU密集型代码:各种循环处理、计数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 63,167
精华内容 25,266
关键字:

多线程和多进程举例