python基础之实现并发和并行

1.多任务

多任务是指在同一时间执行多个任务,多任务的两种表现形式:
1.并发:在同一时间内交替交替多个任务
2.并行:在同一时间内同时去执行多个任务
实现多任务通常有多进程、多线程以及协程

2.进程

进程是资源分配的最小单位,是操作系统进行资源分配的调度运行的基本单位(通俗理解:一个运行中的程序就是一个进程,例如:qq、微信等)

2.1 进程的使用步骤

1.导入进程包
import mutiprocessing

2.创建进程对象
进程对象 = mutiprocessing.Process(args)
args:有三个常用参数,详情如下

参数解释
target执行的目标任务名,这里指的是函数(方法)名
name进程名字,一般不设置
group进程组,目前只能使用None

3.启动进程执行任务
进程对象.start()

2.2.进程单任务和多任务实现的例子

2.2.1单任务

import time
def sing():
    for i in range(3):
        print(f'sing{i}')
        time.sleep(0.5)


def dance():
    for i in range(3):
        print(f'dance{i}')
        time.sleep(0.5)

if __name__ == '__main__':
    t1 = time.time()
    sing()
    dance()
    print(time.time() - t1)

执行结果:
sing0 sing1 sing2 dance0 dance1 dance2 3.0040132999420166

2.2.2多进程实现多任务

import time, multiprocessing
def sing():
    for i in range(3):
        print(f'sing{i}')
        time.sleep(0.5)


def dance():
    for i in range(3):
        print(f'dance{i}')
        time.sleep(0.5)

if __name__ == '__main__':
    t1 = time.time()
    sing_processing = multiprocessing.Process(target=sing)
    dance_processing = multiprocessing.Process(target=dance)
    sing_processing.start()
    dance_processing.start()
    sing_processing.join() #等待子进程结束后再往下执行
    dance_processing.join()
    print(time.time() - t1)

执行结果:
sing0 dance0 sing1 dance1 sing2 dance2 1.7761003971099854

2.3执行带参数的任务

参数名解释
args以元组的形式给任务传参
kwargs以字典的形式给任务传参

注意
1:元组传参一定和参数顺序一致
2:字典传参时字典的key要和参数名一致

import time, multiprocessing
def sing(name, num):
    for i in range(num):
        print(f'{name}在唱歌...')
        time.sleep(0.5)


def dance(name, num):
    for i in range(num):
        print(f'{name}在跳舞...')
        time.sleep(0.5)

if __name__ == '__main__':
    t1 = time.time()
    sing_processing = multiprocessing.Process(target=sing, args=('小明', 3))
    dance_processing = multiprocessing.Process(target=dance, kwargs={'name': '蔡徐坤', 'num': 3})
    sing_processing.start()
    dance_processing.start()
    sing_processing.join() #等待子进程结束后再往下执行
    dance_processing.join()
    print(time.time() - t1)

执行结果:
蔡徐坤在跳舞... 小明在唱歌... 蔡徐坤在跳舞... 小明在唱歌... 蔡徐坤在跳舞... 小明在唱歌... 1.668849229812622

2.4 获取进程编号

当程序中进程的数量越来越多时,如果没有办法区分主进程和子进程还有不同的子进程,那么就无法进行有效的进程管理,为了方便管理实际上每个进程都是有自己的编号的。

1.获取当前进程编号:
os.getpid()

2.获取当前父进程编号:
os.getppid()

import time, multiprocessing, os
def sing(name, num):
    print(f'唱歌进程编号:{os.getpid()}')
    print(f'唱歌父进程编号:{os.getppid()}')
    for i in range(num):
        print(f'{name}在唱歌...')
        time.sleep(0.5)


def dance(name, num):
    print(f'跳舞进程编号:{os.getpid()}')
    print(f'跳舞父进程编号:{os.getppid()}')
    for i in range(num):
        print(f'{name}在跳舞...')
        time.sleep(0.5)

if __name__ == '__main__':
    t1 = time.time()
    sing_processing = multiprocessing.Process(target=sing, args=('小明', 3))
    dance_processing = multiprocessing.Process(target=dance, kwargs={'name': '蔡徐坤', 'num': 3})
    sing_processing.start()
    dance_processing.start()
    sing_processing.join() #等待子进程结束后再往下执行
    dance_processing.join()
    print(time.time() - t1)

执行结果:
唱歌进程编号:14388
跳舞进程编号:2460
唱歌父进程编号:3472
小明在唱歌...
跳舞父进程编号:3472
蔡徐坤在跳舞...
小明在唱歌...
蔡徐坤在跳舞...
小明在唱歌...
蔡徐坤在跳舞...
1.6702723503112793

2.5守护主进程

主进程会等待所有子进程执行结束再结束,设置守护主进程就是在主进程结束后不再执行子进程剩余的工作

import time, multiprocessing

def work():
    for i in range(10):
        print('子进程执行中...')
        time.sleep(0.2)

if __name__ == '__main__':
    # 方法1:multiprocessing.Process(target=work, daemon=True)
    # 方式2:子进程对象.daemon=True
    work_processing = multiprocessing.Process(target=work, daemon=True)
    work_processing.start()
    time.sleep(0.8)
    print('主进程执行结束...')

执行结果:
子进程执行中...
子进程执行中...
子进程执行中...
子进程执行中...
主进程执行结束...

子进程10次还没执行完毕,主进程结束,子进程也会结束

2.6案例-多进程实现视频文件多任务拷贝器

import os
import multiprocessing
def copy_file(source_dir, dest_dir, filename):
    with open(source_dir+ '/' + filename, 'rb') as source_file:
        with open(dest_dir+ '/' +filename, 'wb') as  dest_file:
            while True:
                data = source_file.read(1024)
                if data:
                    dest_file.write(data)
                else:
                    break


if __name__ == '__main__':
    #1.判断目标文件夹是否存在
    if not os.path.exists('copy_file'):
        os.mkdir('copy_file')
    #2.遍历目标文件夹中的文件,获取文件名列表
    dirlist = os.listdir('vidio')
    #3.利用多进程创建多个任务
    for path in dirlist:
        copy_file_processing = multiprocessing.Process(target=copy_file, args=('vidio', 'copy_file', path))
        copy_file_processing.start()
    print('dump!!!')

3.线程

线程是程序执行的最小单位,同属一个进程的多个线程共享进程的全部资源,一个进程至少拥有一个线程。

3.1线程的创建步骤

1.导入线程包
import threading

2.通过线程类创建线程对象
线程对象 = threading.Thread(args)
args:有三个常用参数,详情如下

参数解释
target执行的目标任务名,这里指的是函数(方法)名
name线程名字,一般不设置
group线程组,目前只能使用None

3.启动线程执行任务
线程对象.start()

3.2获取线程id和名称

  • 获取线程名称:threading.currentThread().getName
  • 获取线程id:threading.currentThread().ident

3.3线程的其他描述

线程的用法与进程相同,包括执行带参数的任务、守护主线程,只不过是将进程对象改为线程对象

3.2.1案例-多线程实现视频文件多任务拷贝器

import os
import threading
def copy_file(source_dir, dest_dir, filename):
    with open(source_dir+ '/' + filename, 'rb') as source_file:
        with open(dest_dir+ '/' +filename, 'wb') as  dest_file:
            while True:
                data = source_file.read(1024)
                if data:
                    dest_file.write(data)
                else:
                    break


if __name__ == '__main__':
    #1.判断目标文件夹是否存在
    if not os.path.exists('copy_file'):
        os.mkdir('copy_file')
    #2.遍历目标文件夹中的文件,获取文件名列表
    dirlist = os.listdir('vidio')
    #3.利用多进程创建多个任务
    for path in dirlist:
        copy_file_threading = threading.Thread(target=copy_file, args=('vidio', 'copy_file', path))
        copy_file_threading.start()
    print('dump!!!')

4.进程和线程的关系和区别

4.1关系

1.线程是依附在进程里面的,没有进程就没有线程
2.一个进程可以有多个线程,一个进程默认提供一个进程

4.2区别:

1.创建进程的资源开销要比创建线程开销大
2.进程是操作系统分配资源的基本单位,而线程是CPU调度的基本单位
3.线程不能独立执行,必须依附在进程中

4.3优缺点对比

1.进程

  • 优点:可以使用多核
  • 缺点:资源开销大

2.线程

  • 优点:资源开销小
  • 缺点:不可以使用多核

5.线程池与进程池

存放固定数量的进程/线程,然后把要执行的任务都放置在进程/线程池中,由进程/线程池去决定分配给哪个进程/线程完成任务,可重复使用进程/线程池

5.1进程池的使用

1.导入ProcessPoolExecutor包
from concurrent.futures import ProcessPoolExecutor

2.定义进程池目标函数
def fn(arg1, arg2...)

3.利用进程池执行任务
with ProcessPoolExecutor(num) as t: t.submit(fn, arg1, arg2...)

5.2案例-利用进程池实现视频文件多任务拷贝器

import os
from concurrent.futures import ProcessPoolExecutor
def copy_file(source_dir, dest_dir, filename):
    with open(source_dir+ '/' + filename, 'rb') as source_file:
        with open(dest_dir+ '/' +filename, 'wb') as  dest_file:
            while True:
                data = source_file.read(1024)
                if data:
                    dest_file.write(data)
                else:
                    break


if __name__ == '__main__':
    #1.判断目标文件夹是否存在
    if not os.path.exists('copy_file'):
        os.mkdir('copy_file')
    #2.遍历目标文件夹中的文件,获取文件名列表
    dirlist = os.listdir('vidio')
    #3.开辟50个进程执行任务
    with ProcessPoolExecutor(50) as t:
        for path in dirlist:
            t.submit(copy_file, 'vidio', 'copy_file', path)
    print('dump!!!')

PS:线程池和进程池几乎一样,就把ProcessPoolExecutor更换为ThreadPoolExecutor

6.协程

协程又称为微线程,是多任务的异步操作,协程的作用是在执行函数A的时候,可以中断A函数执行B函数,然后中断B函数继续执行A函数。协程的任务是主动切换的,切换条件是IO,遇到阻塞操作时自动切换,在微观上协程是一个一个任务的切换,在宏观上,我们看到的就是多个任务在执行。
注意:协程所做的一切都是单线程条件下

python对于协程的支持是通过生成器实现的,协程是遵循某些规则的生成器
__next__()启动或者恢复generator的执行,相当于send(None)
send()用于发送值给yield

6.1生成器yield

def test():
    print('start')
    n = 1
    while True:
        r = yield n
        print('ok', r)
        n += 1

if __name__ == '__main__':
    # 创建生成器
    gen = test()
    #启动生成器
    res = gen.__next__()
    #相当于gen.send(None)操作
    print(res) #1
    res = gen.__next__()
    print(res) #2
    res = gen.__next__()
    print(res) #3
    #执行结果:
    '''
       start
        1
        ok None
        2
        ok None
        3 
    '''
    res = gen.send(100)
    print(res)
    # 执行结果
    '''
        ok 100
        4
    '''

    '''

代码解释:
当执行__next__操作的时候,进入内部函数开始执行,当第一次遇到yield(可以理解为return)的时候,在赋值给res之前直接返回,并且立即停止操作,下次__next__的时候再在上次断点的地方进行操作。

6.2async/await实现协程多任务

1.导入asyncio包
import asyncio

2.定义异步函数
async def fn(): pass

3.定义多个任务

async def func1():
    print("你好啊,我是啊一")
    await asyncio.sleep(2)
    print("你好啊,我是啊一")


async def func2():
    print("你好啊,我是啊二")
    await asyncio.sleep(2)
    print("你好啊,我是啊二")


async def func3():
    print("你好啊,我是啊三")
    await asyncio.sleep(2)
    print("你好啊,我是啊三")

4.创建任务列表

  • 创建任务:asyncio.create_task(函数)
  • 任务列表:asyncio.wait(tasks)
import asyncio
# 1.async 声明是个异步函数
async def func1():
    print("你好啊,我是啊一")
    #await:挂起任务,执行下一个任务
    await asyncio.sleep(2) #3.在异步函数中不能出现同步操作,否则异步中断
    print("你好啊,我是啊一")


async def func2():
    print("你好啊,我是啊二")
    await asyncio.sleep(2)
    print("你好啊,我是啊二")


async def func3():
    print("你好啊,我是啊三")
    await asyncio.sleep(2)
    print("你好啊,我是啊三")

async def main():
    tasks = [
        asyncio.create_task(func1()),  #4.asyncio.create_task()创建任务 py3.8以后加上asyncio.create_task()
        asyncio.create_task(func2()), 
        asyncio.create_task(func3())
    ]
    await asyncio.wait(tasks) #5.任务列表


if __name__ == '__main__':
    loop = asyncio.get_event_loop() #6.定义异步对象
    loop.run_until_complete(main()) #7.执行异步函数
    loop.close()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值