多进程、进程池

多进程

  1. 什么是进程

一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。
不仅可以通过线程完成多任务,进程也是可以的

  1. 进程的状态

工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态

  • 就绪态:运行的条件都已经慢去,正在等在cpu执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态
多进程、进程池_第1张图片
image.png

进程的创建-multiprocessing

multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情

例1

# -*- coding:utf-8 -*-
from multiprocessing import Process
import time


def run_proc():
    """子进程要执行的代码"""
    while True:
        print("----2----")
        time.sleep(1)


if __name__=='__main__':
    p = Process(target=run_proc)
    p.start()
    while True:
        print("----1----")
        time.sleep(1)

注意:创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动

例2

# -*- coding:utf-8 -*-
from multiprocessing import Process
import os
import time

def run_proc():
    """子进程要执行的代码"""
    print('子进程运行中,pid=%d...' % os.getpid())  # os.getpid获取当前进程的进程号
    print('子进程将要结束...')

if __name__ == '__main__':
    print('父进程pid: %d' % os.getpid())  # os.getpid获取当前进程的进程号
    p = Process(target=run_proc)
    print(p.name)
    p.start()
  1. Process参数如下:
    Process(group, target, name, args , kwargs)
  • target:如果传递了函数的引用,可以任务这个子进程就执行这里的代码
  • args:给target指定的函数传递的参数,以元组的方式传递
  • kwargs:给target指定的函数传递命名参数
  • name:给进程设定一个名字,可以不设定
  • group:指定进程组,大多数情况下用不到
    Process创建的实例对象的常用方法:
  • start():启动子进程实例(创建子进程)
  • is_alive():判断进程子进程是否还在活着
  • join([timeout]):是否等待子进程执行结束,或等待多少秒
  • terminate():不管任务是否完成,立即终止子进程
    Process创建的实例对象的常用属性:
  • name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
  • pid:当前进程的pid(进程号)
  1. 给子进程指定的函数传递参数
 # -*- coding:utf-8 -*-
from multiprocessing import Process
import os
from time import sleep


def run_proc(name, age, **kwargs):
    for i in range(10):
        print('子进程运行中,name= %s,age=%d ,pid=%d...' % (name, age, os.getpid()))
        print(kwargs)
        sleep(0.2)

if __name__=='__main__':
    p = Process(target=run_proc, args=('test',18), kwargs={"m":20})
    p.start()
    sleep(1)  # 1秒中之后,立即结束子进程
    p.terminate()
    p.join()
  1. 进程间不同享全局变量
from multiprocessing import Process
import time,queue

queue = queue.Queue(200)

def write_data():
    for i in range(0,200):
        global queue
        queue.put(i)

    print(queue.full())

def get_data():
    print('-----')

    global queue
    while queue.empty() is not True:
        print(queue.get())

if __name__ == '__main__':

    process1 = Process(target=write_data)
    process1.start()
    process1.join()

    process2 = Process(target=get_data)
    process2.start()

进程间通信-Queue:

Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。

  1. Queue的使用 可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序
#coding=utf-8
from multiprocessing import Queue
q=Queue(3) #初始化一个Queue对象,最多可接收三条put消息
q.put("消息1") 
q.put("消息2")
print(q.full())  #False
q.put("消息3")
print(q.full()) #True

#因为消息列队已满下面的try都会抛出异常,第一个try会等待2秒后再抛出异常,第二个Try会立刻抛出异常
try:
    q.put("消息4",True,2)
except:
    print("消息列队已满,现有消息数量:%s"%q.qsize())

try:
    q.put_nowait("消息4")
except:
    print("消息列队已满,现有消息数量:%s"%q.qsize())

#推荐的方式,先判断消息列队是否已满,再写入
if not q.full():
    q.put_nowait("消息4")

#读取消息时,先判断消息列队是否为空,再读取
if not q.empty():
    for i in range(q.qsize()):
        print(q.get_nowait())

说明:

  • Queue.qsize():返回当前队列包含的消息数量;
  • Queue.empty():如果队列为空,返回True,反之False ;
  • Queue.full():如果队列满了,返回True,反之False;
  • Queue.get(block, timeout):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;
  • Queue.get_nowait():相当Queue.get(False);
  • Queue.put(item,block,timeout):将item消息写入队列,block默认值为True;
  • Queue.put_nowait(item):相当Queue.put(item, False);

进程池的概念

python中,进程池内部会维护一个进程序列。当需要时,程序会去进程池中获取一个进程。
如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。

multiprocessing.Pool常用函数解析:

  • apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
  • close():关闭Pool,使其不再接受新的任务;
  • terminate():不管任务是否完成,立即终止;
  • join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;

进程池中中进程通信Manager().Queue()

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.

代码

#如何使用进程池
# from multiprocessing import Pool
# import requests

# def download_data_by_page(page,name):
#     print(page,name)
#     #http://blog.jobbole.com/all-posts/page/2/
#     full_url = 'http://blog.jobbole.com/all-posts/page/%s/' % str(page)
#     req_headers = {
#         'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
#     }

#     response = requests.get(full_url,headers=req_headers)
#     if response.status_code == 200:
#         print(response.text)
#         #可以在这里做数据解析


# if __name__ == '__main__':


#     #创建一个进程池
#     process_pool = Pool(4)


#     for page in range(0,1000):
#         #往进程池中添加任务
#         process_pool.apply_async(download_data_by_page,(1,'进程池'))
    

#     #关闭进程池,后面不能再添加任务了
#     process_pool.close()
#     #子进程先执行,执行完毕后,再继续执行主进程代码
#     process_pool.join()


#python自带的进程池模块
from concurrent.futures import ProcessPoolExecutor
import requests

def download_data_by_page(page,name):
    print(page,name)
    
    full_url = 'http://blog.jobbole.com/all-posts/page/%s/' % str(page)
    req_headers = {
        'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
    }

    
    response = requests.get(full_url,headers=req_headers)
    if response.status_code == 200:
        print(response.status_code)
        #可以在这里做数据解析

        return response.text

#可以在回调函数中做数据解析
def download_done(future):
    print(future.result())


if __name__ == '__main__':
    
    #创建一个进程池
    pool = ProcessPoolExecutor(4)

    
    for page in range(0,100):
        handler = pool.submit(download_data_by_page,page,'下载任务')
        handler.add_done_callback(download_done)

    pool.shutdown()
    

# python的多线程:
# 有一个全局解释器锁(GIL):意味着python中的多线程其实是并发的操作

# 对比线程和进程
# 1.定义:
# 进程:是操作系统分配资源和调度的基本单元
# 线程:是依赖于进程执行,线程是cpu执行调度的最小单元

# 2.区别:
# 进程是会分配资源空间,每一个进程之间的资源不共享
# 线程不占用资源空间,线程之间的资源是共享的,为了防止资源错乱,我们一般加线程锁

# 3.使用场景:
# 进程一般情况下处理计算密集型任务
# 线程一般处理I/O密集型任务


你可能感兴趣的:(多进程、进程池)