文章目录

  • Python-多任务
    • 多任务
      • 什么是多任务
      • 多任务理解
    • 线程
      • 线程介绍
      • 线程方法
      • 使用线程完成多任务
      • 类的方式创建线程
      • 多线程共享全局变量
      • 线程同步
    • 进程
      • 进程定义
      • 进程概念
      • 进程与程序区别
      • 进程方法
      • 使用进程完成多任务
      • 通过继承Process类创建进程
      • 多进程共享全局变量
      • 多进程共享全局变量+多进程队列
      • 进程池
      • 多任务文件夹复制
      • 进程与线程区别
    • 协程
      • 协程与线程差异
      • greenlet的使用
      • gevent的使用
    • 最后总结

Python-多任务

多任务

什么是多任务

  • 多任务就是可以同时做多件事情就叫多任务

多任务理解

  • 并发:CPU小于当前的执行的任务,是假的多线程
  • 并行:CPU大于当前的执行的任务,是真的多线程

线程

线程介绍

  • 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
  • 导入:import threading

线程方法

  • start: 启动线程

    • 该线程创建出来为主线程
    • 使用start会等子线程执行结束,然后主线程才结束
  • setDaemon : 守护线程
    • 不会等子线程执行结束
    • 当主线程结束就直接结束
  • join: 等待子线程执行结束
    • 放在start后使用
    • 当任务中存在多线程调用时,会先等子线程执行结束在去执行另外一个子线程
  • threading.enumerate : 查看创建的线程
    • 注意:主线程也算在内
    • 是在start之后创建出来的子线程

使用线程完成多任务

# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2020/12/28"""
总结如下:
线程是在start执行之后创建
start : 启动线程
target : 目标
setDaemon : 守护线程
join : 线程等待
"""
import threading
import timedef sing():for i in range(5):print('我正在唱歌')time.sleep(1)def dance():    # 子线程for i in range(5):print('我正在跳舞')time.sleep(1)# def demo():
#     print('Hello world')if __name__ == '__main__':# target: 目标,调用的对象,th = threading.Thread(target=sing)th1 = threading.Thread(target=dance)# print(threading.enumerate())# setDaemon: 守护线程,不会等子线程执行结束# th.setDaemon(True)# th1.setDaemon(True)# 启动线程th.start()  # 主线程,主线程会等到子线程执行结束th1.start()print(threading.enumerate())# 等待子线程执行结束# th.join()# th1.join()# 查看线程数量# 为什么结果却为3个呢# 因为还有一个主线程在内,存在两个子线程print(threading.enumerate())

类的方式创建线程

  • 当想用类的方式创建的时候,需要在类中即成thread.Thread(看导入的情况而定)
  • 如果想进行传参的话,需要使用到super函数来而定
  • 当想在类中使用多线程,要在类中的run方法中实现
  • 通过start来启动多线程
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2020/12/28import threading
import timeclass Animal(threading.Thread):def run(self) -> None:print('this is animal')time.sleep(1)if __name__ == '__main__':for i in range(5):r = Animal()r.start()print(threading.enumerate())

多线程共享全局变量

  • 线程之间可以共享全局变量,但是会出现计算出错。可通过锁来解决这么一系列问题
  • 互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性
  • 多线程之间传参可通过args来进行
  • mutex = threading.Lock() : 创建锁
  • mutex.acquire() : 上锁操作
  • mutex.release() : 解锁操作
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2020/12/29import threading
import time
import dis
num = 100"""
首先要知道,Python中的多线程并非真正的多线程,
Python中的线程也并非采用单个执行来运行多线程,
而Python中的多线程采用的是时间,轮转来执行,什么意思呢?
意思就是当第一个线程执行的差不多了,然后把一个线程扔出去,让第一个线程等着,
在把第二个线程继续执行,执行的差不多的时候,在把第二个线程扔出去,让第二个线程等着,
所以这就出现了线程之间的共享全局变量的一个问题,如何解决这个问题呢。
可以通过锁来解决这么一个问题的出现
"""import threading# 创建锁
mutex = threading.Lock()def demo(nums):global num# 上锁操作mutex.acquire()for i in range(nums):num += 1# 解锁操作mutex.release()print(f'demo--{num}')def demo1(nums):global num# 上锁操作mutex.acquire()for i in range(nums):num += 1mutex.release()print(f'demo1--{num}')if __name__ == '__main__':# 多线程之间传参,可以通过args# 传入多参数要以元祖的形式t = threading.Thread(target=demo, args=(1000000, ))t1 = threading.Thread(target=demo1, args=(1000000,))t.start()t1.start()time.sleep(2)print(f'main--{num}')# def funt(a):
#     a += 1
#     print(a)
#
# print(dis.dis(funt))

线程同步

  • 线程同步我们需要使用threading.Condition()完成线程同步
# 线程同步
cond = threading.Condition()# 等待
cond.wait()# 唤醒
cond.notify()

实现效果:

  • 天猫精灵:小爱同学
  • 小爱同学:在
  • 天猫精灵:现在几点了?
  • 小爱同学:你猜猜现在几点了
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2020/12/29import threading"""
天猫精灵:小爱同学
小爱同学:在
天猫精灵:现在几点了?
小爱同学:你猜猜现在几点了
"""# 线程同步
cond = threading.Condition()class Tmall(threading.Thread):def __init__(self, name):super(Tmall, self).__init__(name=name)def run(self) -> None:cond.acquire()  # 上锁操作print(f'{self.name} : 小爱同学')cond.wait()print(f'{self.name} : 现在几点了?')cond.notify()# 解锁操作cond.release()class love(threading.Thread):def __init__(self, name):super(love, self).__init__(name=name)def run(self) -> None:cond.acquire()print(f'{self.name} : 在')cond.notify()cond.wait()print(f'{self.name} : 你猜猜现在几点了')cond.release()if __name__ == '__main__':tianmao = Tmall(name='天猫精灵')xiaoai = love(name='小爱同学')tianmao.start()xiaoai.start()

进程

进程定义

  • 进程是计算机中的程序关于某数据集合上的一次运动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。并且进程是线程的容器。

进程概念

  • 进程是一个实体。每一个进程都有它自己的地址空间。
  • 进程是一个执行中的程序
  • 进程是操作系统中最基本、重要的概念
  • 任务调度和执行的基本单位
  • 多个进程同时执行的顺序是随机的

进程与程序区别

  • 进程:正在执行的程序。动态的,暂时的
  • 程序:没有执行的代码,是一个静态的,永久的

进程方法

  • 通过multiprocessing.Process模块
  • group : 参数未使用,默认值为None
  • target : 表示调用对象,即子线程要执行的任务
  • args : 表示调用的位置参数元祖
  • kwargs : 表示调用对象的字典
  • name : 子进程名称

使用进程完成多任务

  • 注意: 多线程同时执行的顺序是随机的
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2020/12/31"""
总结如下:
什么是多线程?
多线程就是相当于就是操作系统运行多个任务,
进程是线程的容器,一个进程最少要有一个线程
进程是正在执行的程序,动态的
"""# 导入进程
import multiprocessing
# 当想导入进程队列时,需要从multiprocessing导入
# 安全队列
from multiprocessing import queues
import timedef test():while True:print('1')time.sleep(1)def test1():while True:print('2')time.sleep(1)def test2():while True:print('3')time.sleep(1)if __name__ == '__main__':# Process : 创建进程的方法t = multiprocessing.Process(target=test)t1 = multiprocessing.Process(target=test1)t2 = multiprocessing.Process(target=test2)# 启动多线程t.start()t1.start()t2.start()print(4)

通过继承Process类创建进程

class Demo(multiprocessing.Process):def run(self):while True:print("--1--")time.sleep(1)if __name__ == '__main__':p1 = Demo()p1.start()

多进程共享全局变量

  • 注意⚠️: 多进程之间是不能进行共享全局变量的,但想通过共享全局变量,只能通过多进程的queue队列(注意:这里说的是多进程的队列,而不是普通的队列)
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2021/1/5import multiprocessing
import time"""
总结如下:
多进程之间是不能进行共享全局变量的
"""
num = 100def num1():global numnum += 100print(f'num1 for {num}')    # 200def num2():print(f'num2 for {num}')    # 100if __name__ == '__main__':m1 = multiprocessing.Process(target=num1)m2 = multiprocessing.Process(target=num2)# 启动多进程m1.start()m2.start()

多进程共享全局变量+多进程队列

  • 使用多进程队列可以解决这么个问题
  • q = multiprocessing.Queue() : 创建进程队列
  • q.get() : 从队列取值
  • q.put(): 将一个数据存入队列
  • q.qsize() : 返回队列的大小
  • q.empty() : 判断队列是否为空
  • q.full() : 判断队列是否满了
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2021/1/5import multiprocessing
import time"""
总结如下:
多进程之间是不能进行共享全局变量的
当想进行共享全局变量
"""
num = 100def num1(q):global numnum += 100q.put(num)  # 200print(f'num1 for {num}')    # 200def num2(q):# print(f'num2 for {num}')    # 100data = q.get()  # 200print(f'num2 for {data}')if __name__ == '__main__':q = multiprocessing.Queue()m1 = multiprocessing.Process(target=num1, args=(q, ))m2 = multiprocessing.Process(target=num2, args=(q, ))# 启动多进程m1.start()m2.start()

进程池

  • 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但是如果是上百甚至上千个目标,手动的去创建的进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法,也就是进程池。
  • apply_asyn : 非阻塞,异步形式
  • close : 关闭进程池,使其不在接受新的任务
  • join : 主进程阻塞,等待子进程的退出,join方法要在close后使用
  • 进程池之间通信使用的是:q = multiprocessing.Manager().Queue()
# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2021/1/5import multiprocessingdef demo1(q):q.put("a")def demo2(q):data = q.get("a")print(data)if __name__ == '__main__':# 当使用进程池的时候并不能使用进程使用的队列# 需要使用到进程池的队列q = multiprocessing.Manager().Queue()# 创建进程池po = multiprocessing.Pool(2)# 异步启动,非阻塞po.apply_async(demo1, args=(q, ))po.apply_async(demo2, args=(q, ))# 同步启动,阻塞# po.apply()# 关闭进程池po.close()# 等待子进程池结束# 调用join之前,先调用close函数,否则会出错# 执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束po.join()

多任务文件夹复制

# -*- coding: utf-8 -*-
# @Author: Small-J
# @Date  : 2021/1/5"""
获取用户要复制的文件夹名字
创建一个新的文件夹
获取文件夹所有待拷贝的文件名字
创建进程池
添加拷贝任务
"""import multiprocessing
import os
import timedef copy_file(old_folder_name, new_folder_name, file_name, q):"""复制文件内容"""with open(old_folder_name + '/' + file_name, 'rb')as f:content = f.read()with open(new_folder_name + '/' + file_name, 'wb')as f1:f1.write(content)def main():# 获取用户要复制的文件夹名字old_folder_name = input('请输入你要创建的文件夹名字:')# 创建一个新的文件夹new_folder_name = old_folder_name + '[附件]'if not os.path.exists(new_folder_name):os.mkdir(new_folder_name)# 获取文件夹中所有的内容名字file_name_list = os.listdir(old_folder_name)# 创建进程池队列q = multiprocessing.Manager().Queue()# 创建进程池po = multiprocessing.Pool(2)# 添加拷贝任务for file_name in file_name_list:po.apply_async(copy_file, args=(old_folder_name, new_folder_name, file_name, q))# 关闭进程池po.close()# 等待子进程执行结束po.join()all_file_num = len(file_name_list)copy_ok_num = 1while True:copy_ok_num += 1time.sleep(1)print("\r复制的进度为:%.2f %%" % (copy_ok_num * 100 / all_file_num), end='')if copy_ok_num >= all_file_num:breakif __name__ == '__main__':main()

总结如下:

  • 也存在着线程池和进程池
  • IO密集型适用于多线程
  • 计算密集型适用多进程
  • 多线程无法利用多核CPU发挥优势,会轮流使用CPU来发挥优势
  • 多进程是利用多核CPU发挥优势

进程与线程区别

  • 根本区别

    • 进程: 操作系统资源分配的基本单位
    • 线程: 任务调用和执行的基本单位
  • 开销
    • 进程: 通过复制代码+资源创建子进程,每个进程都有独立的代码和数据空间,程序之间的切换会有较大的开销
    • 线程: 在同一份代码里,创建线程,共享内存,开销较小。
  • 分配内存
    • 进程:系统在运行的时候为每个进程分配不同的内存空间
    • 线程: 线程所使用的资源是它所属的进程的资源
  • 包含关系
    • 进程: 一个进程可以拥有多个线程
    • 线程: 线程是进程的一部分

协程

  • 协程,又称为微线程, 它是实现多任务的另一种方式,只不过是比现场更小的执行单位。因为它自带CPU的上下文,这样只要在合适的时机,我们就可以把一个协程切换到另一个协程。

协程与线程差异

  • 线程:每个线程都有自己换成Cache等等数据,操作系统还会做这些数据的恢复操作,所以线程的切换非常消耗性能
  • 协程:单纯的操作CPU的上下文,所以一秒切换上百万次系统都能扛住。说哟完成多任务效率比线程和进程都高
# 使用yield来实现协程
import timedef task():while True:print('--1--')time.sleep(1)yield 'task'def task1():while True:print('--2--')time.sleep(1)yield 'task2'def main():t1 = task()t2 = task1()# print(t2)     # 对象地址值# print(t1)# next(t1)# next(t2)while True:next(t1)next(t2)if __name__ == '__main__':main()

greenlet的使用

  • greenlet也是实现协程的一种方式,但是它实现协程并不会手动的去切换,还需要手动的实现。
  • 安装:pip install greenlet
# -*- coding: utf-8 -*-
# Author : Small-J
# 2021/1/8 11:44 上午from greenlet import greenlet
import timedef demo1():while True:print("demo1")gr2.switch()time.sleep(0.5)def demo2():while True:print("demo2")# 通过switch到其他协程地方,该协程会被挂起gr1.switch()time.sleep(0.5)if __name__ == '__main__':gr1 = greenlet(demo1)gr2 = greenlet(demo2)gr1.switch()

gevent的使用

  • 比greenlet更强大的并且能够主动切换任务的模块gevent
  • 安装: pip install gevent
  • 原理:当一个greenlet遇到IO操作时,比如访问网络,就主动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行
  • 由于IO操作非常耗时,经常使程序处于等待状态,有了genvet为我们自动切换协程,就保证总有greenlet在运行
  • 在爬取图片等操作的时候适用于领域
# -*- coding: utf-8 -*-
# Author : Small-J
# 2021/1/8 11:55 上午import gevent
import time# 破解time.sleep延迟补丁,除了time,各种延迟都适配
from gevent import monkey# 补丁
monkey.patch_all()
"""
总结如下:
如果使用time.sleep就会变成同步的形式去执行,先是f1执行完之后在执行但如果想time.sleep被识别的话,就需要导入monkey模块需要使用gevent.sleep来实现,为什么需要呢?因为需要一种延迟的效果来展示,如网络请求延迟也可以
"""def f1(n):for i in range(n):print(gevent.getcurrent(), i)# gevent.sleep(0.5)time.sleep(0.5)def f2(n):for i in range(n):print(gevent.getcurrent(), i)# gevent.sleep(0.5)time.sleep(0.5)if __name__ == '__main__':# 创建协程# g1 = gevent.spawn(f1, 5)# g2 = gevent.spawn(f2, 5)# g1.join()# g2.join()# 简化上面的操作# greenlets, timeout=None, raise_error=False, count=Nonegevent.joinall([gevent.spawn(f1, 5),gevent.spawn(f2, 5)])

最后总结

  • 进程是资源分配的单位
  • 线程是操作系统调度的单位
  • 进程切换需要的资源最大,效率很低
  • 线程切换需要的资源一般,效率一般
  • 协程切换任务资源很小,效率高
  • 多线程、多进程根据CPU核数不一样可能是并行的,但是协程是在一个线程中,所以是并发

Python-多任务总结相关推荐

  1. python多任务,线程详解

    python 多任务 多线程 python的thread模块是⽐较底层的模块,python的threading 模块是对thread做了⼀些包装的,可以更加⽅便的被使⽤ 调用 1 直接调用 # –*– ...

  2. Python多任务(1.多进程的概念和用法 )

    1.进程的概念 2.进程的状态: 3.python中的进程包  multiprocessing 4.多进程的简单使用 5.获取进程的编号 6.进程执行带有参数的任务 7.进程的注意点 8. 进程.线程 ...

  3. Python多任务——线程

    多线程-threading python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用 1. 使用threading模块 ...

  4. python多任务-线程

    多任务介绍 多任务就是多个程序或者函数同时运行(就是操作系统可以同时运行多个任务),比如你可以边吃饭边刷抖音边抖腿吃饭. 多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务.由于C ...

  5. python 多任务

    一些概念 1.多任务 简单地说,就是同时可以运行多个任务.打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务. 2.并行 指的是任务数小于等于cpu核数,在一段时间内 ...

  6. python多任务、面向对象、命令行启动动态绑定端口号静态web服务器代码实现

    一.静态web服务器-多任务 多任务web服务器:使用多线程,比进程更节省资源,支持多用户同时访问,可以同时处理多个客户端请求 实现步骤 若客户端与服务端建立连接,则创建子线程,使用子线程处理客户端请 ...

  7. python多任务编程

    文章目录 进程 (Process) 多进程编程 multiprocessing 模块创建进程 创建自定义进程类 进程池技术 进程间通信 (IPC) 管道通信 Pipe 消息队列 共享内存 信号通信 信 ...

  8. 沙师弟学Python多任务之线程

    什么叫多任务呢? 在现实生活中,有很多场景是同时进行的,比如唱歌跳舞,试想如果把这两者分开来,该有多滑稽. 多任务其实就是操作系统同时处理多个任务,处理多任务得有多核处理器,所以在多核处理器普及之后, ...

  9. python 多任务、线程、协程

    多任务: 初始: 多任务就是同一时刻多个任务同时执行,例如开演唱会时明星一边唱歌一边跳舞,开车时眼睛看路手操作方向盘.这些都是多任务场景. 对于电脑来说多任务就是同时运行多个应用程序,例如qq.微信. ...

  10. python多任务—协程(一)

    写在前面: 花了一周的时间,对协程做了一个简单的梳理,特别是异步编程asyncio库的使用,做了详细的说明.本文主要包括的知识点有:yield生成器的复习并实现协程的功能.greenlet库实现协程. ...

最新文章

  1. 使用Keil语言的嵌入式C编程教程(上)
  2. php自定义函数出现乱码,php的imagettftext 函数出现乱码的解决方法
  3. 让文本垂直居中的几个方法
  4. repeater 时间格式化
  5. .NET Core开发实战(第33课:集成事件:使用RabbitMQ来实现EventBus)--学习笔记(上)...
  6. linux系统启动故障排除
  7. Spring Boot+JPA 有查询条件的查询
  8. 怒拒Facebook:语音识别大神、Kaldi之父将加盟小米
  9. c语言中如何通过二级指针来操作二维数组
  10. sqli-labs Less-11 and Less-12
  11. android音量知识总结
  12. nginx HTML网页乱码
  13. 使用DX查看系统配置
  14. 浙大 PAT 甲级 1077 Kuchiguse
  15. Modbus转Profinet网关连接英威腾变频器方法
  16. 概率论与数理统计--大数定律与中心极限定理
  17. JavaFx教程-02第一个javaFX程序
  18. GitHub无法访问下载
  19. 城市中计算机控制系统的应用,城市污水处理厂计算机控制系统
  20. 批处理系统,分时系统,分布式系统,多处理/多核处理器系统,集群系统,嵌入式操作系统

热门文章

  1. OpenGL入门demo
  2. Java格式化输出时间
  3. Android 实时视频采集
  4. 多种退出vim的方法
  5. Kettle工具的使用
  6. CocosCreator3D鼠标拖拽相机旋转,第一人称旋转
  7. java 消息服务框架_Java消息服务 在 Spring Boot 中的使用
  8. bert 论文阅读笔记
  9. gensim中的word2vec的使用
  10. 围剿Viking、专杀工具的较量——继初遇Worm.Viking.dy