Python3多进程与多线程区别及使用(1.进程)

进程和线程
是什么:
进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程,或者更专业化来说:进程是指程序执
行时的一个实例。
线程是进程的一个实体。
进程——资源分配的最小单位,线程——程序执行的最小单位。
线程进程的区别体现在几个方面:
第一:因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。
第二:体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。
3.属于同一个进程的所有线程共享该进程的所有资源,包括文件描述符。而不同过的进程相互独立。
4.线程又称为轻量级进程,进程有进程控制块,线程有线程控制块;
5.线程必定也只能属于一个进程,而进程可以拥有多个线程而且至少拥有一个线程;
第四:体现在程序结构上,举一个简明易懂的列子:当我们使用进程的时候,我们不自主的使用if else嵌套来判断pid,使得程序结构繁琐,但是当我们使用线程的时候,基本上可以甩掉它,当然程序内部执行功能单元需要使用的时候还是要使用,所以线程对程序结构的改善有很大帮助。
进程与线程的选择取决以下几点:
1、需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。
2、线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程
序的响应
3、因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程;
4、并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;
5、需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。
 
 
多进程
模块:Multiprocessing
(原文有些许错误,本文笔记处已修改,未笔记处自行留意)
 
简述:
Process类
用来描述一个进程对象
star()方法启动进程
join()方法实现进程间的同步,等待所有进程退出
close()方法用来阻止多余的进程涌入进程池Pool造成进程阻塞
 
1 multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
target是函数名字
args是函数需要的参数,以tuple的形式传入
import multiprocessing
import os


def run_proc(name):
    print('Child process {0} {1} Running '.format(name, os.getpid()))


if __name__ == '__main__':
    print('Parent process {0} is Running'.format(os.getpid()))
    for i in range(5):
        p = multiprocessing.Process(target=run_proc, args=(str(i),))
        print('process start')
        p.start()
    p.join()
    print('Process close')



结果:
Parent process 5408 is Running
process start
process start
process start
process start
process start
Child process 0 1044 Running
Child process 1 1120 Running
Child process 3 10824 Running
Child process 2 9292 Running
Child process 4 10528 Running

 

Pool(进程池)
Pool 可以提供指定数量的进程供用户使用,默认是 CPU 核数。当有新的请求提交到 Poll 的
时候,如果池子没有满,会创建一个进程来执行,否则就会让该请求等待。
 
- Pool 对象调用 join 方法会等待所有的子进程执行完毕
- 调用 join 方法之前,必须调用 close
- 调用 close 之后就不能继续添加新的 Process 了
pool=Pool(numprocess,initializer,initargs)
numproxess:需要创建的进程个数,如果忽略将使用cpu_count()的值。即系统上的CPU数量。
initializer:每个进程启动时都要调用的对象。
initargs:为initalizer传递的参数。

 

apply_async(要调用的方法,参数列表,关键字参数列表):使用非阻塞方式调用指定方法,并行执行(同时执行)

apply(要调用的方法,参数列表,关键字参数列表):使用阻塞方式调用指定方法,,阻塞方式就是要等上一个进程退出后,下一个进程才开始运行。

close():关闭进程池,不再接受进的进程请求,但已经接受的进程还是会继续执行。

terminate():不管程任务是否完成,立即结束。

join():主进程堵塞(就是不执行join下面的语句),直到子进程结束,注意,该方法必须在close或terminate之后使用。

pool.map(func,iterable,chunksize):将可调用对象func应用给iterable的每一项,然后以列表形式返回结果,
通过将iterable划分为多块,并分配给工作进程,可以并行执行。chunksize指定每块中的项数,
如果数据量较大,可以增大chunksize的值来提升性能。

pool.map_async(func,iterable,chunksize,callback):与map方法不同之处是返回结果是异步的,
如果callback指定,当结果可用时,结果会调用callback。

pool.imap(func,iterable,chunksize):与map()方法的不同之处是返回迭代器而非列表。

pool.imap_unordered(func,iterable,chunksize):与imap()不同之处是:结果的顺序是根据从工作进程接收到的时间而定的。

pool.get(timeout):如果没有设置timeout,将会一直等待结果,
如果设置了timeout,超过timeout将引发multiprocessing.TimeoutError异常。

pool.ready():如果调用完成,返回True

pool.successful():如果调用完成并且没有引发异常,返回True,如果在结果就绪之前调用,将引发AssertionError异常。

pool.wait(timeout):等待结果变为可用,timeout为等待时间。

 

pool.apply_async
apply_async方法用来同步执行进程,允许多个进程同时进入池子。
import multiprocessing
import os
import time
def run_task(name):
  print('Task {0} pid {1} is running, parent id is {2}'.format(name, os.getpid(), os.getppid()))
  time.sleep(1)
  print('Task {0} end.'.format(name))
if __name__ == '__main__':
  print('current process {0}'.format(os.getpid()))
  #设定池内进程数
  p = multiprocessing.Pool(processes=3)
  for i in range(6):
    p.apply_async(run_task, args=(i,))
  print('Waiting for all subprocesses done...')
  p.close()
  p.join()
  print('All processes done!') 
结果
current process 562
Task 0 pid 778 is running, parent id is 562
Task 1 pid 779 is running, parent id is 562
Task 2 pid 780 is running, parent id is 562
Waiting for all subprocesses done...
Task 1 end.
Task 0 end.
Task 2 end.
Task 3 pid 779 is running, parent id is 562
Task 4 pid 778 is running, parent id is 562
Task 5 pid 780 is running, parent id is 562
Task 4 end.
Task 5 end.
Task 3 end.
All processes done!
 
pool.apply
apply(func[,args[,kwds]]))
该方法只能允许一个进程进入池子,在一个进程结束后,另一个进程才可以进入池子
import multiprocessing
import os
import time
def run_task(name):
  print('Task {0} pid {1} is running, parent id is {2}'.format(name, os.getpid(), os.getppid()))
  time.sleep(1)
  print('Task {0} end.'.format(name))
if __name__ == '__main__':
  print('current process {0}'.format(os.getpid()))
  p = multiprocessing.Pool(processes=3)
  for i in range(6):
    p.apply(run_task, args=(i,))
  print('Waiting for all subprocesses done...')
  p.close()
  p.join()
  print('All processes done!')
结果:
current process 562
Task 0 pid 785 is running, parent id is 562
Task 0 end.
Task 1 pid 786 is running, parent id is 562
Task 1 end.
Task 2 pid 787 is running, parent id is 562
Task 2 end.
Task 3 pid 785 is running, parent id is 562
Task 3 end.
Task 4 pid 786 is running, parent id is 562
Task 4 end.
Task 5 pid 787 is running, parent id is 562
Task 5 end.
Waiting for all subprocesses done...
All processes done!

 

Queue进程间通信
Queue 用来在多个进程间通信。Queue 有两个方法,get 和 put。
 
put 方法
Put 方法用来插入数据到队列中,有两个可选参数,blocked 和 timeout。
- blocked = True(默认值),timeout 为正
该方法会阻塞 timeout 指定的时间,直到该队列有剩余空间。如果超时,抛出 Queue.Full 异常。
blocked = False
如果 Queue 已满,立刻抛出 Queue.Full 异常
get 方法
get 方法用来从队列中读取并删除一个元素。有两个参数可选,blocked 和 timeout
- blocked = True(默认),timeout 正值
等待时间内,没有取到任何元素,会抛出 Queue.Empty 异常。
blocked = False
Queue 有一个值可用,立刻返回改值;Queue 没有任何元素,会抛出 Queue.Empty 异常。
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def proc_write(q,urls):
  print('Process(%s) is writing...' % os.getpid())
  for url in urls:
    q.put(url)
    print('Put %s to queue...' % url)
    time.sleep(random.random())
# 读数据进程执行的代码:
def proc_read(q):
  print('Process(%s) is reading...' % os.getpid())
  while True:
    url = q.get(True)
    print('Get %s from queue.' % url)
if __name__=='__main__':
  # 父进程创建Queue,并传给各个子进程:
  q = Queue()
  proc_writer1 = Process(target=proc_write, args=(q,['url_1', 'url_2', 'url_3']))
  proc_writer2 = Process(target=proc_write, args=(q,['url_4','url_5','url_6']))
  proc_reader = Process(target=proc_read, args=(q,))
  # 启动子进程proc_writer,写入:
  proc_writer1.start()
  proc_writer2.start()
  # 启动子进程proc_reader,读取:
  proc_reader.start()
  # 等待proc_writer结束:
  proc_writer1.join()
  proc_writer2.join()
  # proc_reader进程里是死循环,无法等待其结束,只能强行终止:
  proc_reader.terminate()

 

Pipe 进程间通信
常用来在两个进程间通信,两个进程分别位于管道的两端。
multiprocessing.Pipe([duplex])
 两个示例
from multiprocessing import Process, Pipe
def send(pipe):
  pipe.send(['spam',42, 'egg'])  # send 传输一个列表
  pipe.close()
if __name__ == '__main__':
  (con1, con2) = Pipe()              # 创建两个 Pipe 实例
  sender = Process(target=send, args=(con1, ))   # 函数的参数,args 一定是实例化之后的 Pip 变量,不能直接写 args=(Pip(),)
  sender.start()                  # Process 类启动进程
  print("con2 got: %s" % con2.recv())       # 管道的另一端 con2 从send收到消息
  con2.close()   

结果

con2 got: ['spam', 42, 'egg']

 

from multiprocessing import Process, Pipe


def talk(pipe):
    pipe.send(dict(name='Bob', spam=42))  # 传输一个字典
    reply = pipe.recv()  # 接收传输的数据
    print('talker got:', reply)


if __name__ == '__main__':
    (parentEnd, childEnd) = Pipe()  # 创建两个 Pipe() 实例,也可以改成 conf1, conf2
    child = Process(target=talk, args=(childEnd,))  # 创建一个 Process 进程,名称为 child
    child.start()  # 启动进程
    print('parent got:', parentEnd.recv())  # parentEnd 是一个 Pip() 管道,可以接收 child Process 进程传输的数据
    parentEnd.send({x * 2 for x in 'spam'})  # parentEnd 是一个 Pip() 管道,可以使用 send 方法来传输数据
    child.join()  # 传输的数据被 talk 函数内的 pip 管道接收,并赋值给 reply
    print('parent exit')

结果:

parent got: {'name': 'Bob', 'spam': 42}
talker got: {'pp', 'mm', 'aa', 'ss'}
parent exit

 

更多共享数据方式:
multiprocessin:Array,Value (shared memory)  和 Manager(Server process manager)
 
Server process manager比 shared memory 更灵活,因为它可以支持任意的对象类型。另外,一个单独的manager可以通过进程在网络上不同的计算机之间共享,不过他比shared memory要慢。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2019-05-13 11:30  IMWU  阅读(...)  评论(...编辑  收藏