进程的基本使用

1、 进程的概念

进程(Process)是资源分配的最小单位,也是线程的容器。
进程:一个程序运行起来后,代码+用到的资源称之为进程,他是操作系统分配资源的基本单元。

2、进程的状态

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

在这里插入图片描述

  • 就绪态:运行的条件都已经满足,正在等待cup执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态

3、进程的创建——multiprocessing

'''
1、导入模块
2、通过模块提供的process类创建进程对象
3、进程对象.start()
'''
# 1、导入模块
import multiprocessing
import time
def work1():
    for i in range(10):
        print("正在执行work1",i)
        time.sleep(0.5)



if __name__ == '__main__':

    # 2、通过模块提供的process类创建进程对象
    pro_obj=multiprocessing.Process(target=work1)
    # 3、进程对象.start()
    pro_obj.start()

在这里插入图片描述

4、进程名称和pid

获取进程名称:
multiprocessing.current_process()

获取进程编号:
multiprocessing.current_process().pid
导入os模块后: os.getpid()

'''
1、导入模块
2、通过模块提供的process类创建进程对象
3、进程对象.start()
'''
# 1、导入模块
import multiprocessing
import time
import os
def work1():
    for i in range(10):
        print("子线程名称:",multiprocessing.current_process())
        print("子线程id:",multiprocessing.current_process().pid)
        print("子线程id:",os.getpid())
        #获取进程的父id
        print("子线程的id:%s,子线程的父亲id:%s"%(os.getpid(),os.getppid()))

        time.sleep(0.5)



if __name__ == '__main__':
    #获取主进程的名称
    print(multiprocessing.current_process())
    #获取进程的编号
    print("主进程编号:", multiprocessing.current_process().pid)
    #使用os模块获取编号
    print("主进程编号:",os.getpid())
    # 2、通过模块提供的process类创建进程对象
        #target指定子进程要执行的分支函数
        #name 指定子进程的名称
    pro_obj=multiprocessing.Process(target=work1,name="p1")
    # 3、进程对象.start()
    pro_obj.start()

5、进程参数、全局变量问题

5.1、子进程参数传递

进程传递参数的方式和线程传递参数的方式基本一致

import multiprocessing
import time

def work1(a,b,c):
    print("参数:",a,b,c)

if __name__ == '__main__':
    #1)使用args
    pro_obj=multiprocessing.Process(target=work1,args=(1,2,3))
    # #2)使用kwargs,参数的顺序可以与形参不一致
    pro_obj=multiprocessing.Process(target=work1,kwargs={"a":1,"c":2,"b":3})
    #混合args 和 kwargs
    pro_obj=multiprocessing.Process(target=work1,args=(1,),kwargs={"c":3,"b":2})
    pro_obj.start()

5.2、全局变量及共享问题

进程间是不能共享全局变量的
因为:子进程会复制主进程的资源到内部去运行

import multiprocessing
import time
num=0
def work1():
    global  num
    for i in range(10):
        num+=1
    print("work1-----",num)
def work2():
    print("work2-----",num)

if __name__ == '__main__':
    pro_obj1=multiprocessing.Process(target=work1)
    pro_obj2=multiprocessing.Process(target=work2)
    pro_obj1.start()
    pro_obj2.start()
    time.sleep(1)
    print("main------",num)
#结果
work1----- 10
work2----- 0
main------ 0

6、守护进程

通过daemon设置子进程守护主进程

  • p1.daemon=True 设置子进程p1守护主进程,当主进程结束的时候,子进程也随之结束
  • p1.terminate()终止进程执行,并非是守护进程
import multiprocessing
import time

def work1():

    for i in range(10):
        print("work1------")
        time.sleep(0.5)


if __name__ == '__main__':
    pro_obj1=multiprocessing.Process(target=work1)
    #守护线程
    pro_obj1.daemon=True
    pro_obj1.start()
    time.sleep(2)
    print("主线程结束")

7、线程和进程的对比

进程:能够完成多任务,比如在一台电脑上能够同时运行多个QQ
线程:能够完成多任务,比如一个QQ中的多个聊天窗口

使用区别

  • 进程是系统进行资源分配和调度的一个独立单位
  • 线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
  • 一个程序至少有一个进程,一个进程至少有一个线程。
  • 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    在这里插入图片描述
  • 线程不能独立运行,必须依存在进程中
对比维度多进程多线程总结
数据共享、同步数据共享复杂,需要用IPC;数据是分开的,同步简单因为共享进程数据,数据共享简单,但是也因为这个原因导致同步复杂各有优势
内存、CPU占用内存多,切换复杂,CPU利用率低占用内存少,切换简单,CPU利用率高线程占优
创建销毁、切换创建销毁、切换复杂,速度慢创建销毁、切换简单,速度很快线程占优
编程、调试编程简单,调试简单编程复杂,调试复杂进程占优
可靠性进程间不会相互影响一个线程挂掉导致整个线程挂掉进程占优
分布式适应于多核、多机分布式;如果一台机器不够,扩展到多台机器比较到简单适用于多核分布式进程占优

选择原则:

  • 需要频繁创建销毁的优先使用线程(如:web服务器)
  • 线程的切换快,所以在需要大量计算,切换频繁时用线程(如图像处理、算法处理)
  • 因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布的用线程
  • 需要更安全时,适合选择进程;需要速度时,选择进程更好

8、消息队列

可以使用multiprocessing模块中的Queue实现多进程之间的数据传递
Queue本身是一个消息列队程序

8.1、创建队列并且放入值

'''
队列是multiprocessing模块提供的
1)创建队列(指定长度)
2)放值
3)取值
'''
import multiprocessing
# 1)创建队列(指定长度)
#queue=multiprocessing.Queue(n) n表示队列长度
queue=multiprocessing.Queue(5)
# 2)放值
#queue.put(值)
queue.put(1)
queue.put("hello")
queue.put([1,2,3])
queue.put((1,2,3))
queue.put({"a":10,"b":11,"c":12})
#定义的队列长度为5,放入第6个数据后,队列就进行了阻塞状态
# 默认等待队列先取出值,后再放入新的值
# queue.put({"a":11,"b":11,"c":12})

# 3)取值
#queue.get(值)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)
value=queue.get()
print(value)
print("-"*20)

#当队列已经空的时候,再次取值,程序会进入阻塞状态,等待放入新的值到队列再取
# value=queue.get()
# print(value)
# print("-"*20)

8.2、消息队列-常见判断

1、判断是否已满
队列.full()
2、判断是否已经为空
队列.empty()
3、取出队列中消息的个数
队列.qsize()

8.3、Queue实现进程间的通信

在这里插入图片描述

'''
1、准备两个进程
2、准备一个队列 ,一个进程向队列中写入数据
3、另一个进程进行读数据,就完成进程间的通信
'''
import multiprocessing
import time
#写入数据到队列的函数
def input(queue):
    for i  in range(10):
        if queue.full():
            print("队列已满")
            break
        queue.put(i)
        print("%s写入成功"%i)
#读取队列数据并显示的函数i
def output(queue):
    while True:
        if queue.qsize()==0:
            print("队列已空")
            break
        value=queue.get()
        print("取出数据:",value)

if __name__ == '__main__':

    #创建一个空的队列
    queue=multiprocessing.Queue(3)
    #创建两个线程
    pro_obj1=multiprocessing.Process(target=input,args=(queue,))
    pro_obj2=multiprocessing.Process(target=output,args=(queue,))
    pro_obj1.start()
    pro_obj1.join()
    pro_obj2.start()

9、进程池

9.1、进程池概述

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果数量较多,手动的取创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法

在这里插入图片描述

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请;但如果池中的进程数已经达到最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。

'''
1、创建一个函数,用于模拟文件拷贝
2、创建一个进程池,长度3(表示进程池中最多创建三个进程)
3、先用进程池同步方式拷贝文件
4、再用异步的方法拷贝文件
'''
import multiprocessing
import time

# 1、创建一个函数,用于模拟文件拷贝
def copy_work():

    print("正在拷贝文件...",multiprocessing.current_process())
    time.sleep(1)
# 2、创建一个进程池,长度3(表示进程池中最多创建三个进程)
if __name__ == '__main__':
    #创建进程池
    pool=multiprocessing.Pool(3)
    for i in range(10):
       # 3、先用进程池同步方式拷贝文件
       # 进程池同步方式pool.apply(函数名,(参数1,参数2...))
       #pool.apply_(copy_work)

       # 4、再用异步的方法拷贝文件
       #如果使用apply_async方式
       #1)pool.close()表示不再接收新的任务
       #2)主进程不在等待进程池执行结束后再退出,需要进程池join()
       #3)pool.join()让主进程等待j进程池执行接收后再退出
       pool.apply_async(copy_work)
    #pool.close()表示不再接收新的任务
    pool.close()
    pool.join()

9.2、进程池中的Queue实现进程间的通信

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue()

用法:queue=multiprocessing.Manager().Queue(3)

'''
1、准备两个进程
2、准备一个队列 ,一个进程向队列中写入数据
3、另一个进程进行读数据,就完成进程间的通信
'''
import multiprocessing
import time

def input(queue):
    for i in range(10):
        if queue.full():
            print("队列已满")
            break
        queue.put(i)
        print("队列写入:",i)
def output(queue):
    for i in range(10):
        if queue.qsize()==0:
            print("队列已空")
            break
        queue.get(i)
        print("取出元素:",i)

if __name__ == '__main__':
    #创建进程池
    pool=multiprocessing.Pool(3)
    #c创建进程池中的队列
    queue=multiprocessing.Manager().Queue(5)
    #使用进程池执行任务

        #同步方式
    # pool.apply(input,(queue,))
    # pool.apply(output,(queue,))

        #异步方式
    #apply_async()返回值ApplyResult对象,该对象有一个wait()的方法
    #wait()方法类似join()表示先让当前进程执行完毕,后续进程才能启动
    result=pool.apply_async(input,(queue,))
    result.wait()
    pool.apply_async(output,(queue,))

    pool.close()
    pool.join()

10、案例:文件拷贝器

'''
1、定义变量、保存源文件夹、目录文件夹所在的路径
2、在目标路经创建新的文件夹
3、获取源文件夹中的所有文件
4、遍历列表,得到文件夹中的所有文件名
5、定义函数进行函数拷贝

文件拷贝函数:
参数:源文件夹目录,目标文件夹路径、文件名
1、拼接源文件和目录的具体路径
2、打开源文件,创建目录文件
3、读取源文件的内容,写入到目标文件
'''
import os
import multiprocessing
def copy_work(source_dir,dest_dir,file_name):
    # 1、拼接源文件和目录的具体路径
    # 2、打开源文件,创建目录文件
    source_path=source_dir+'/'+file_name
    dest_path=dest_dir+"/"+file_name
    print(source_path+"----->"+dest_path)
    # 3、读取源文件的内容,写入到目标文件
    with open(source_path,"rb") as source_file:
        #创建目标文件
        with open(dest_path,"wb") as dest_file:
            while True:
                # 读源文件保存到目标文件
                file_data = source_file.read(1024)
                #判断文件是否读取完
                if file_data:
                    dest_data=dest_file.write(file_data)
                else:
                    break

if __name__ == '__main__':
    # 1、定义变量、保存源文件夹、目录文件夹所在的路径
    source_dir="其他文件"
    dest_dir="C:/Users/haoli/Desktop/test"
    # 2、在目标路经创建新的文件夹
    try:
        os.mkdir(dest_dir)
    except Exception as e:
        print("文件夹已经存在")
    # 3、获取源文件夹中的所有文件
    file_list=os.listdir(source_dir)
    pool=multiprocessing.Pool(3)
    # 4、遍历列表,得到文件夹中的所有文件名
    for file_name in file_list:
        # 5、定义函数进行函数拷贝
        pool.apply_async(copy_work,args=(source_dir,dest_dir,file_name))

    pool.close()
    pool.join()
#结果
其他文件/1.txt----->C:/Users/haoli/Desktop/test/1.txt
其他文件/2.txt----->C:/Users/haoli/Desktop/test/2.txt
其他文件/3.txt----->C:/Users/haoli/Desktop/test/3.txt
其他文件/4.txt----->C:/Users/haoli/Desktop/test/4.txt
其他文件/5.txt----->C:/Users/haoli/Desktop/test/5.txt
其他文件/6.txt----->C:/Users/haoli/Desktop/test/6.txt
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值