深入理解Python中的进程线程

  1. 进程的概念
  2. 并行与并发的区别
  3. 同步与异步的概念
  4. 进程创建的方式
  5. 父进程和子进程的关系
  6. 线程的相关概念
  7. Python中进程池的相关概念
  8. Python的的回调函数
  9. 进程池+回调函数的实际应用(网络爬虫)

1、进程的概念

什么是进程—>CPU在同一时刻只能处理一个任务,只是因为cpu执行速度很快。 
cpu在各个任务之间来回的进行切换。 
进程的概念:正在进行的一个过程或者说一个任务,而负责执行任务的则是CPU,进程本身是 
一个抽象的概念,即进程就是一个过程、一个任务。 
CPU描述的是一个程序的执行过程. 
进程之间是如何做到并发的:CPU在各个任务之间来回的进行切换,并在切换的过程当中保存当前 
进程的执行状态(保存蛋糕的执行过程)。 
进程与程序的区别:程序相当于菜谱,而进程相当于做菜的整个过程。 
需要强调的是:同一个程序执行两次(双击),那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放a,一个可以播放b. 
核的概念:https://zhidao.baidu.com/question/541410131.html 
处理器,就是说有几个处理器。。。也就说一个CPU里面会有几个处理器,这样就可以同时处理几个要求了。。。

2、并行与并发的区别

无论是并行还是并发,在用户看来都是同时运行的,不管是进程还是线程,都只是一个任务而已, 
真正干活的是CPU,CPU来做这些任务,而一个cpu(单核)同一时刻只能执行一个任务。 
并行:多个任务同时运行,只有具备多个cpu才能实现并行,含有几个cpu,也就意味着在同一时刻可以执行几个任务。 
并发:是伪并行,即看起来是同时运行的,实际上是单个CPU在多道程序之间来回的进行切换。

3、同步与异步的概念

同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。 
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进行处理,这样可以提高执行的效率。 
打电话的过程就是同步通信,发短信时就是异步通信。

4、进程创建的方式

用户创建出来的所有进程都是由操作系统负责的,因此无论是哪一种创建进程的方式,实际上都是调用操作系统的接口创建的,进程的切换都是由操作系统控制的。 
无论哪一种创建进程的方式,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。

5、父进程和子进程之间的关系

子进程创建后,父进程和子进程有各自不同的地址空间,多道技术要求物理层面实现进程之间内存的 
隔离,任何一个进程在其地址空间的修改都不会影响到另外一个进程。 
注意:子进程和父进程之间是可以有只读的共享的内存区域的。 
进程与进程之间数据(资源)是隔离的,两个进程之间可以基于管道这种方式进行通信。在Unix当中,是含有进程层次的概念的,但是在windows当中,是没有进程层次的概念的,所有的进程都是地位相同的。 
在Linux当中,每启动一个命令,都会启动一个进程。

6、线程的相关概念

6、线程的概念 
一个进程里面至少有一个控制线程,进程的概念只是一种抽象的概念,真正在CPU上面调度的是进程 
里面的线程,就好比真正在地铁这个进程里面工作的实际上是地铁里面的线程,北京地铁里面至少要有 
一个线程,线程是真正干活的,线程用的是进程里面包含的一堆资源,线程仅仅是一个调度单位,不包含资源。

7 什么时候需要开启多个线程? 
什么时候需要开启多个线程:一个进程里面的多个线程共享这个进程里面的资源,因此如果多个任务共享同一块资源的时候,需要开启多个线程。 
多线程指的是,在一个进程中开启多个线程,简单的说:如果多个任务共用同一个资源空间,那么必须在一个进程内开启多个线程。

8一个进程里面需要包含多个线程? 
一个进程这个任务里面可能对应多个分任务,如果一个进程里面只开启一个线程的话,多个分任务之间实际上是串行的执行效果,即一个程序里面只含有一条执行路径。

9多线程和多进程的关系 
对于计算密集型应用,应该使用多进程;对于IO密集型应用,应该使用多线程。 
线程的创建比进程的创建开销小的多。

10、Python中线程的特点 
在其他语言当中,一个进程里面开启多个线程,每个线程都可以给一个cpu去使用,但是在 
python当中,在同一时刻,一个进程当中只能有一个线程处于运行状态。 
eg:在其他语言当中,比如我现在开启了一个进程,这个进程当中含有几个线程,如果我现在有多个cpu,每一个线程是可以对应相应的CPU的。 
但是在python当中,如果我们现在开启了一个进程,这个进程里面对应多个线程,同一时刻只有一个 
线程可以处于运行状态。 
对于其他语言而言,在多CPU系统中,为了最大限度的利用多核,可以开启多个线程。 
但是Python中的多线程是利用不了多核优势的。

进程与线程的区别: 
在同一个进程当中,多个线程彼此之间可以相互通信;但是进程与进程之间的通信必须基于IPC这种 
消息的通信机制(IPC机制包括队列和管道)。 
在一个进程当中,改变主线程可能会影响其它线程的行为,但是改变父进程并不会影响其它子进程 
的行为,因为进程与进程之间是完全隔离的。 
在python当中,在同一时刻同一进程当中只能同时有一个线程在运行,如果有一个线程使用了 
系统调用而阻塞,那么整个进程都会被挂起。

7、Python中进程池的相关概念

重要功能:

def apply(self, func, args=(), kwds={}):
    '''
    Equivalent of `func(*args, **kwds)`.
    '''
    assert self._state == RUN
    return self.apply_async(func, args, kwds).get()

作用:
在进程池中同步(打电话)的提交任务,若提交的前一个任务没有执行完,后一个任务则不能执行.
此时进程池中的任务将变为串行的效果.


def apply_async(self, func, args=(), kwds={}, callback=None,
        error_callback=None):
    '''
    Asynchronous version of `apply()` method.
    '''
    if self._state != RUN:
        raise ValueError("Pool not running")
    result = ApplyResult(self._cache, callback, error_callback)
    self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
    return result


作用:
1.在进程池中异步(发短信)的提交任务,若提交的前一个任务没有执行完,后一个任务可以继续执行.
此时进程池中的任务将变为并行的效果.
2.在进程池当中虽然可以创建出同步对象,但是进程池当中最多只能同步的执行num个任务.
3.在进程池当中对象创建的顺序就是任务提交的顺序,但是创建之后并不一定得到执行,进程池中始终维护
num个任务在执行。
4.进程池当中始终共享着开始创建的那num个进程,减小了创建进程的开销.
5.向进程池中提交任务的顺序是一定的,但是进程池中任务执行的顺序是不一定的。


pool = Pool(processes=4):
Pool([numprocess  [,initializer [, initargs]]]):创建进程池
其中numprocess代表要创建的进程数,如果省略,将默认使用cpu_count()的值。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

代码示例:http://blog.csdn.net/a2011480169/article/details/73602708

8、Python中回调函数的概念

引言: 
之前所有并发的执行一个函数,都没有把函数的结果给拿回来。 
如何异步的获取结果呢? 通过get()函数即可。 
方式:收集进程池中的对象,并进行get()获取结果。 
代码示例:

#!/usr/bin/python
# -*- coding:utf-8 -*-

from multiprocessing import Pool
def work(n):
    return n**2


if __name__ == '__main__':
    pool = Pool()
    res_l = []

    for i in range(6):
        res = pool.apply_async(func=work,args=(i,))
        res_l.append(res)

    print("进程池当中创建的对象列表是:")
    for res in res_l:
        print(res)
    print(len(res_l))


    print("进程池当中任务(函数的运行的结果是:)")
    for res in res_l:
        print(res.get())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行结果:

进程池当中创建的对象列表是:
<multiprocessing.pool.ApplyResult object at 0x0000000002ED17F0>
<multiprocessing.pool.ApplyResult object at 0x0000000002ED18D0>
<multiprocessing.pool.ApplyResult object at 0x0000000002ED19B0>
<multiprocessing.pool.ApplyResult object at 0x0000000002ED1A90>
<multiprocessing.pool.ApplyResult object at 0x0000000002ED1B70>
<multiprocessing.pool.ApplyResult object at 0x0000000002ED1C50>
6
进程池当中任务(函数的运行的结果是:)
0
1
4
9
16
25

Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

回调函数的相关概念:

回调函数(apply_async的扩展用法)
回调函数:若将A函数的执行结果交给B函数作为输入,此时A函数称为为回调函数(有点类似于管道)。

1、不需要回调函数的场景:如果在主进程中等待进程池中所有任务都执行完毕后,再统一处理结果,则无需回调函数
此时在进程池当中获取函数运行结果的方式:收集进程池中的对象,并进行get()获取结果。

代码示例:
参考上面的代码,统一获取进程池中的对象,然后通过get获取相关结果


2、需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我
的结果了,主进程则调用一个函数去处理该结果,该函数即回调函数
我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回
调函数时就省去了I/O的过程,直接拿到的是任务的结果。

猜你喜欢

转载自blog.csdn.net/hanyuyang19940104/article/details/80242928