Python中进程与线程的(详细)教程之进程

多任务原理

什么叫多任务? ? ?

  • 现代操作系统(Windows. Mac OS、Linux、 UNIX等)都支持多任务
  • 操作系统同时可以运行多个任务
单核CPU实现多任务原理

操作系统轮流让各个任务交替执行,QQ执行2us,切换到微信,在执行2us, 再切换到陌陌,执行.2…表面是看,每个任务反复执行下去
但是CPU调度执行速度太快了,导致我们感觉就像所有任务都在同时执行一样

多核CPU实现多任务原理

真正的并性执行多任务,只能在多核CPU上实现,但是由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行

  • 并发:看上去一起执行,任务书数多于CPU核心数
  • 并行:真正起执行,任务数小于等于CPU核心数

实现多任务的方式:

  1. 多进程模式
  2. 多线程模式
  3. 协程模式
  4. 多进程+多线程模式

进程

对于操作系统而言,一个任务就是一个进程。

进程是系统中程序执行和资源分配的基本单位,每个进程都有属于自己的数据段、代码段、堆栈段,互不干扰,互不影响

单任务现象
import time

def run():
    while True:
        print("aaa")
        time.sleep(1.2)

if __name__ == "__main__":
    while True:
        print("jack")
        time.sleep(1)

    # 永远也不会执行到run方法,只有上面的while循环结束才可以执行。
    # 这是单任务现象。

    run()

运行结果:
在这里插入图片描述

永远也执行不到上面的run方法中的aaa,这就是单线程,只能等一个执行完了再执行另一个

启动进程实现多任务
import os
from multiprocessing import Process
import time


# 子进程需要执行的代码
def run(value):
    while True:
    
        # os.getpid()获取当前进程的id号
        # os.getppid()获取当前进程的父进程的id号
        print("aaa----%s----%s----%s" % (value, os.getpid(), os.getppid()))
        time.sleep(1.2)

if __name__ == "__main__":
    print("主(父)进程启动 %s" % (os.getpid()))
    
    # 创建子进程
    # target说明进程执行的任务
    # args可以传参,因为是元组,所以只传一个参数要加逗号
    p = Process(target=run, args=("ok",))
    # 启动进程
    p.start()
    
    while True:
        print("jack")
        time.sleep(1)

运行结果:
在这里插入图片描述

可以很明显的看到,两个是同时进行的,因为停顿的秒数不同,但确确实实是两个while True在运行,很神奇,另外(target=run),方法是需要加括号的,只需要传入方法名就行

父进程和子进程的先后顺序
from multiprocessing import Process
import time

def run():
    print("子进程开始")
    time.sleep(2)
    print("子进程结束")

def eat():
    print("开始吃")
    time.sleep(1)
    print("吃完了")


if __name__ == "__main__":
    print("父进程开始")
    p = Process(target=run)
    p.start()

    p1 = Process(target=eat)
    p1.start()

    # 可以发现,父进程根本也没有等子进程执行完在结束,而是各执行各的
    time.sleep(1)
    print("父进程结束")

运行结果:
在这里插入图片描述

可以发现,父进程根本也没有等子进程执行完在结束,而是各执行各的,但是这并不符合常规
正常的应该是老板父进程)指挥员工子进程)做事

让父进程等待子进程执行完之后再结束
from multiprocessing import Process
import time

def run():
    print("子进程开始")
    time.sleep(2)
    print("子进程结束")

def eat():
    print("开始吃")
    time.sleep(1)
    print("吃完了")


if __name__ == "__main__":
    print("父进程开始")
    p = Process(target=run)
    p.start()

    p1 = Process(target=eat)
    p1.start()
    
	# 要想让父进程等待子进程执行完在结束,在下面加个.join()
    p.join()
    p1.join()
  
    time.sleep(1)
    print("父进程结束")

运行结果:
在这里插入图片描述

启动大量子进程

查看电脑核心数的方法
1、打开任务管理器
在这里插入图片描述
2、点击性能
在这里插入图片描述
3、查看核心数,上面显示的是6核心12个逻辑处理器,说明我们可以同时进行12个任务
在这里插入图片描述
例子:

import os
import random
import time
from multiprocessing import Pool


def run(name):
    print("子进程%d启动--%s" % (name, os.getpid()))
    start = time.time()
    time.sleep(random.choice([1, 2, 3]))
    end = time.time()
    print("子进程%d结束--%s--耗时%.2f" % (name, os.getpid(), end - start))

if __name__ == "__main__":
    print("父进程启动")
    # 创建多个进程
    # 进程池
    # Pool(n)  n表示可以同时执行的进程数量,不写的话,默认大小为CPU核心数
    pool = Pool(4)
    for i in range(1, 6):
        # 创建进程,放入进程池统一管理
        pool.apply_async(run, args=(i,))

    # 在调用join之前必须先调用close,调用close之后就不能再继续添加新的进程了
    pool.close()
    pool.join()
    print("父进程结束")

运行结果:
在这里插入图片描述

请看仔细一点,在进程2结束的瞬间,进程5启动,而且他们用的是同一个进程号,是这个进程工作完立马去工作下一个!
笔者因为偷懒,所以用循环处理一种方法,如果要进行不同的进程
例子:

# 我们可以这样
if __name__ == "__main__":
    print("父进程启动")
    # 因为要执行的进程大于我们池子的容量,才看得出效果
    pool = Pool(2)
    # 定义不同的进程
	pool.apply_async(run1)
    pool.apply_async(run2)
    pool.apply_async(run3)
    pool.close()
    pool.join()
    print("父进程结束")

因为笔者比较偷懒,故没有作演示,请见谅

全局变量在多个进程中不能共享
from multiprocessing import Process
import time

num = 100

def run():
    print("子进程开始")
    # 若想在函数内部对函数外的变量进行操作,就需要在函数内部声明其为global
    global num
    num += 1
    time.sleep(2)
    print("子进程结束")

def eat():
    print("开始吃")
    time.sleep(3)
    print("吃完了")

if __name__ == "__main__":
    print("父进程开始")
    print(num)
    p = Process(target=run)
    p.start()
    p.join()
    # 在子进程中修改全局变量对父进程中的全局变了没有影响
    # 在创建子进程时对全局变量做了一个备份,父进程中的与子进程中的num是两个完全不同的两个变量
    # 两个子进程中也不能共享
    print("父进程结束")

运行结果:
在这里插入图片描述

可以看到,num数并没有改变,所以每个子进程都是单独工作的,全局变量在多个进程中不能共享

单进程拷贝文件

文件是15个空白的文本文档

import os
import time

def copy(readPath, writePath):
    # 读取两个文件
    fr = open(readPath, "rb")
    fw = open(writePath, "wb")
    # 读并写入
    context = fr.read()
    fw.write(context)
    # 关闭
    fr.close()
    fw.close()

path = r"D:\新建文件夹\copy"
topath = r"D:\新建文件夹\tocopy"

# 读取path下的所有的文件
fileList = os.listdir(path)

# 启动for循环处理每一个文件
start = time.time()
for fileName in fileList:
    copy(os.path.join(path, fileName), os.path.join(topath, fileName))
end = time.time()
print("总耗时:%.5f" % (end - start))

运行结果:

总耗时:0.00400
多进程拷贝文件
import os
import time
from multiprocessing import Pool

def copy(readPath, writePath):
    # 读取两个文件
    fr = open(readPath, "rb")
    fw = open(writePath, "wb")
    # 读并写入
    context = fr.read()
    fw.write(context)
    # 关闭
    fr.close()
    fw.close()

path = r"D:\新建文件夹\copy"
topath = r"D:\新建文件夹\tocopy"

if __name__ == "__main__":
    # 读取path下的所有的文件
    fileList = os.listdir(path)
    start = time.time()
    pool = Pool(6)
    for fileName in fileList:
        pool.apply_async(copy, args=(os.path.join(path, fileName), os.path.join(topath, fileName)))
    pool.close()
    pool.join()
    end = time.time()
    print("总耗时:%.5f" % (end - start))

运行结果:

总耗时:0.19804

对比一下,是不是很惊讶,为什么多进程比单进程慢呢?

  • 极少的数据,多进程会比单进程,只有的数据才能体现出优势
  • 但是要是100个进程一起工作,理论上也不会快很多,因为因为多进程启动也费时间和资源,销毁也费时间与资源

我们现在测试一下2G,27个资源

  • 单进程:总耗时:19.93352
  • 多进程:总耗时:15.03159

总而言之,多进程理论上是比单进程快些

封装进程对象

我们先创建进程类
名字叫jackProcess.py

import time
from multiprocessing import Process
import os

# 继承自Process
class JackProcess(Process):
    def __init__(self, name):
        Process.__init__(self)
        self.name = name

    def run(self):
        print("子进程(%s--%s)启动" % (self.name, os.getpid()))
        time.sleep(2)
        print("子进程(%s--%s)结束" % (self.name, os.getpid()))

然后再把类实例化
创建一个新文件

from jackProcess import JackProcess


if __name__ == "__main__":
    print("父进程启动")
	# 创建子进程
    pool = JackProcess("test")
    # 自动调用pool进程对象的run方法
    pool.start()
    pool.join()
    print("父进程结束")

运行结果:
在这里插入图片描述

不知道大家有没有注意,我们这里并没有调用run方法,然后也可以执行,这是为什么呢?
因为我们使用pool.start的时候,它会自动调用类里面的方法,很方便,如果有100个方法需要我们去调用,那把进程封装进对象,这样无疑是最方便最快速的

进程间通信

进程中的子进程是不能共享数据的,但是能互相拿数据和给数据
multiprocessing中的Queue
相当于把子进程A的东西传到队列中去,然后子进程B再从队列中拿数据

from multiprocessing import Process, Queue
import os
import time

def write(queue):
    print("启动写子进程--%s" % (os.getpid()))
    for i in range(1,5):
    	# 把数据给队列
        queue.put(i)
        time.sleep(1)
    print("结束写子进程--%s" % (os.getpid()))


def read(queue):
    print("启动读子进程--%s" % (os.getpid()))
    while True:
    	# 从队列里面拿数据,True是一直拿
        value = queue.get(True)
        print("value = " + str(value))
    print("结束读子进程--%s" % (os.getpid()))

if __name__ == "__main__":
    print("父进程开始")
    # 父进程创建队列,并传递给子进程
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))

    # 虽然这两个先是pw在前,但是不一定执行的时候是pw在前
    pw.start()
    pr.start()

    pw.join()
    print("父进程结束")

为什么拿要用死循环呢?因为read方法如果从队列里面拿的话,根本不知道什么时候拿,拿什么,所以我们要一直拿
运行结果:
在这里插入图片描述

有发现,我们的程序一直都在运行,这是为什么呢?因为我们的read方法是死循环,根本结束不了,所以我们要强制性结束

	pw.join()
    # pr进程里面是个死循环,无法等待其结束,只能强行结束
    pr.terminate()
    print("父进程结束")

运行结果:
在这里插入图片描述
如果有错误或者有疑问,请私信我喔,感谢观看

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值