多线程与多进程的比较

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程与多进程的比较相关的知识,希望对你有一定的参考价值。

如果和进程相比较,多线程有什么优点?为什么具有这些优点?

参考技术A 什么是多线程:

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

使用线程的好处有以下几点:

·
使用线程可以把占据长时间的程序中的任务放到后台去处理

·
用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处
理,可以弹出一个进度条来显示处理的进度

·
程序的运行速度可能加快

·
在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比
较游泳了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

====


什么是多进程:

进程是程序在计算机上的一次执行活动。
当你运行一个程序,
你就启动了一个进
程。显然,程序是死的
(
静态的
)
,进程是活的
(
动态的
)
。进程可以分为系统进程
和用户进程。
凡是用于完成操作系统的各种功能的进程就是系统进程,
它们就是
处于运行状态下的操作系统本身;
用户进程就不必我多讲了吧,
所有由你启动的
进程都是用户进程。进程是操作系统进行资源分配的单位。


Windows
下,进程又被细化为线程,也就是一个进程下有多个能独立运行的
更小的单位。

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

多任务带来的好处是明显的,比如你可以边听
mp3
边上网,
与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。
那么这里就涉及到并行的问题,
俗话说,
一心不能二用,这对计算机也一样,

则上一个
CPU
只能分配给一个进程,以便运行这个进程。我们通常使用的计算
机中只有一个
CPU
,也就是说只有一颗心,要让它一心多用,同时运行多个进
程,就必须使用并发技术。实现并发技术相当复杂,最容易理解的是

时间片轮
转进程调度算法

,它的思想简单介绍如下:在操作系统的管理下,所有正在运
行的进程轮流使用
CPU

每个进程允许占用
CPU
的时间非常短
(
比如
10
毫秒
)

这样用户根本感觉不出来
CPU
是在轮流为多个进程服务,就好象所有的进程都
在不间断地运行一样。
但实际上在任何一个时间内有且仅有一个进程占有
CPU


如果一台计算机有多个
CPU

情况就不同了,
如果进程数小于
CPU
数,
则不同
的进程可以分配给不同的
CPU
来运行,这样,多个进程就是真正同时运行的,
这便是并行。但如果进程数大于
CPU
数,则仍然需要使用并发技术。


Windows
中,进行
CPU
分配是以线程为单位的,一个进程可能由多个线程
组成,这时情况更加复杂,但简单地说,有如下关系:

总线程数
<= CPU
数量:并行运行

总线程数
> CPU
数量:并发运行

并行运行的效率显然高于并发运行,所以在多
CPU
的计算机中,多任务的效率
比较高。但是,如果在多
CPU
计算机中只运行一个进程
(
线程
)
,就不能发挥多
CPU
的优势。

这里涉及到多任务操作系统的问题,
多任务操作系统
(

Windows)
的基本原理是
:
操作系统将
CPU
的时间片分配给多个线程
,
每个线程在操作系统指定的时间片内
完成
(
注意
,
这里的多个线程是分属于不同进程的
).
操作系统不断的从一个线程
的执行切换到另一个线程的执行
,
如此往复
,
宏观上看来
,
就好像是多个线程在一
起执行
.
由于这多个线程分属于不同的进程
,
因此在我们看来
,
就好像是多个进程
在同时执行
,
这样就实现了多任务
.Whoops
,真绕口
.
参考技术B 多线程与多进程的区别_百度文库:
https://wenku.baidu.com/view/fdcfbb6aeff9aef8951e0661.html

Python多线程与多进程

多进程

上一章:Python多线程与多进程(一)

 由于GIL的存在,Python的多线程并没有实现真正的并行。因此,一些问题使用threading模块并不能解决

不过Python为并行提供了一个替代方法:多进程。在多进程里,线程被换成一个个子进程。每个进程都运作着各自的GIL(这样Python就可以并行开启多个进程,没有数量限制)。需要明确的是,线程都是同一个进程的组成部分,它们共享同一块内存、存储空间和计算资源。而进程却不会与它们的父进程共享内存,因此进程间通信比线程间通信更为复杂

多进程相比多线程优缺点如下:

优点 缺点
可以使用多核操作系统 更多的内存消耗
进程使用独立的内存空间,避免竞态问题 进程间的数据共享变得更加困难
子进程容易中断 进程间通信比线程困难
避开GIL限制  

 

  

Python多进程

multiprocessing模块提供了一个Process类,它有点类似多线程模块中threading.Thread类。因此,把多线程代码迁移到多进程还是比较简单的,因为代码的基本结构不变

我们快速演示一个多进程的示例:

import multiprocessing


def run(pname):
    print(pname)


for i in range(3):
    p = multiprocessing.Process(target=run, args=("Process-%s" % i,))
    p.start()
    p.join()

    

运行结果:

Process-0
Process-1
Process-2

  

可以看出,多进程和多线程的代码非常像,这里需要注意一点,如果是在Windows上执行的需要把启动进程的代码放到if __name__ == "__main__":底下

if __name__ == "__main__":
    for i in range(3):
        p = multiprocessing.Process(target=run, args=("Process-%s" % i,))
        p.start()
        p.join()

  

进程退出状态:当进程结束的时候,会产生一个状态码,它是一个数字,表示执行结果,不同数字代表程序运行的不同情况:

  • 等于0表示正常完结
  • 大于0表示异常完结
  • 小于0表示进程被另一个进程通过-1*exit_code信号终结

 

下面的代码演示如何读取和使用退出码

import multiprocessing
import time


def first():
    print("There is no problem here")


def second():
    raise RuntimeError("Error raised!")


def third():
    time.sleep(3)
    print("This process will be terminated")


workers = [multiprocessing.Process(target=first), multiprocessing.Process(target=second),
           multiprocessing.Process(target=third)]
for w in workers:
    w.start()

workers[-1].terminate()

for w in workers:
    w.join()

for w in workers:
    print(w.exitcode)

  

运行结果:

There is no problem here
Process Process-2:
Traceback (most recent call last):
  File "/home/lf/anconda3/lib/python3.6/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/home/lf/anconda3/lib/python3.6/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "test01.py", line 11, in second
    raise RuntimeError("Error raised!")
RuntimeError: Error raised!
0
1
-15

  

我们注意到,第三个子进程的print语句没有执行,这是因为在sleep方法结束之前进程已经被中止了。还有一点需要注意的是:两个独立的for循环处理三个子进程:一个启动子进程,另一个通过join方法连接进程。如果我们在开启每个子进程时都执行join方法,而不是没有join直接中断第三个进程,那么第三个进程就不会失败。于是第三个子进程返回的退出码也是0,因为和多线程一样,join方法在目标进程完结之前会阻塞子进程的调用  

进程池

多进程模块还提供了pool类,表示一个进程池,里面装有子进程,可以通过不同的方法执行同一组任务。

Pool类的主要方法如下:

  • apply:这个方法在独立的子进程中运行一个函数。它还会在被调用函数返回结果之前阻塞进程
  • apply_async:这个方法会在独立子进程中异步地运行一个函数,就是说进程会立即返回一个ApplyResult对象,要获得真实的返回值需要使用get()方法。get()在异步执行的函数结束之前都会被阻塞
  • map:这个方法对一组数值应用一个函数。它是一个阻塞动作,所以返回值是每个值经过函数映射的列表

进程间通信:进程间通信的方式不像线程间通信那么简单,但是,Python提供了一些工具帮助我们解决问题。

Queue类是一个既线程安全又进程安全的先进先出(FIFO)数据交换机制。multiprocessing提供的Queue类基本是Queue.Queue的克隆版本,因此二者API基本相同

from multiprocessing import Queue, Process
import random


def generate(q):
    while True:
        value = random.randrange(10)
        q.put(value)
        print("Value added to queue: %s" % (value))


def reader(q):
    while True:
        value = q.get()
        print("Value from queue: %s" % (value))


queue = Queue()
p1 = Process(target=generate, args=(queue,))
p2 = Process(target=reader, args=(queue,))
p1.start()
p2.start()

  

Pipe方法:Pipe(管道)方法为两个进程提供了一种双向通信的机制,Piped()函数返回一对连接对象,每个对象表示管道的一端。每个连接对象都有send()和recv()方法

from multiprocessing import Pipe, Process
import random


def generate(pipe):
    while True:
        value = random.randrange(10)
        pipe.send(value)
        print("Value sent: %s" % (value))


def reader(pipe):
    f = open("output.txt", "w")
    while True:
        value = pipe.recv()
        f.write(str(value))
        print(".")


input_p, output_p = Pipe()
p1 = Process(target=generate, args=(input_p,))
p2 = Process(target=reader, args=(output_p,))
p1.start()
p2.start()

  

多进程也有事件Event,它们的工作方式与多线程类似,只是有一点需要记住,事件对象不能被传递到子进程的函数中,这样做会导致运行时错误,信号机制只能在主进程中被子进程共享:

from multiprocessing import Pool, Event
import time

event = Event()
event.set()


def worker(i):
    if event.is_set():
        time.sleep(0.1)
        print("A - %s" % (time.time()))
        event.clear()
    else:
        time.sleep(0.1)
        print("B - %s" % (time.time()))
        event.set()


pool = Pool(3)
pool.map(worker, range(9))

  

 

以上是关于多线程与多进程的比较的主要内容,如果未能解决你的问题,请参考以下文章

分析详解python多线程与多进程区别

分析详解python多线程与多进程区别

太好了, 终于梳理清楚Python多线程与多进程

多线程与多进程介绍

Python多线程与多进程

python多进程与多线程使用场景