进程、线程补充与协程相关介绍

补充点

1.死锁

当你知道锁的使用抢锁必须要释放锁,其实你在操作锁的时候也极其容易产生死锁现象(整个程序卡死 阻塞)

from threading import Thread, Lock
import time


mutexA = Lock()
mutexB = Lock()
# 类只要加括号多次 产生的肯定是不同的对象
# 如果你想要实现多次加括号等到的是相同的对象 单例模式


class MyThead(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print('%s 抢到A锁'% self.name)  # 获取当前线程名
        mutexB.acquire()
        print('%s 抢到B锁'% self.name)
        mutexB.release()
        mutexA.release()
        
    def func2(self):
        mutexB.acquire()
        print('%s 抢到B锁'% self.name)
        time.sleep(2)
        mutexA.acquire()
        print('%s 抢到A锁'% self.name)  # 获取当前线程名
        mutexA.release()
        mutexB.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyThead()
        t.start()

2.递归锁

递归锁的特点:

可以被连续的acquire和release
但是只能被第一个抢到这把锁执行上述操作
它的内部有一个计数器 每acquire一次计数加一 每realse一次计数减一
只要计数不为0 那么其他人都无法抢到该锁

# 代码只需要将上述死锁的
mutexA = Lock()
mutexB = Lock()
# 换成
mutexA = mutexB = RLock()
# 这样死锁的程序就可以正常运行下去

3.信号量

信号量在不同阶段可能对应不同的技术点,在并发编程中,信号量指的是锁!!

信号量与互斥锁的比较:信号量可以看成是多个坑位,互斥锁只能是一个坑位!!

信号量的具体语法:

from threading import Thread, Semaphore  # 信号量的模块
import time,random

sm = Semaphore(5)  # 表示同时开设5个坑位(互斥锁只开设一个)

def task(name):
    sm.acquire()  # 加锁
    print('%s正在蹲坑'%name)
    time.sleep(random.randint(1,3))  # 蹲坑时间不等
    sm.release()  # 释放锁

if __name__ == '__main__':
    for i in range(20):  # 20个人去抢5个厕所
        t = Thread(target=task,args=(i,))
        t.start()

4.Event事件

主进程/线程等待子进程/线程运行结束时,我们使用的是join方法!

而需要子进程/线程之间的相互的等待结束,需要用到event事件

from threading import Thread,Event
import time

# 1.先产生一个event对象
event = Event()

def person():
    print('你来了')
    time.sleep(2)
    event.set()  # 2.发送信号,表示我执行完毕了,你可以结束了

def my():
    event.wait()  # 3.接收到信号,好的我开始执行
    print('我走了')

if __name__ == '__main__':
    t = Thread(target=person)
    t1 = Thread(target=my)
    t1.start()
    t.start() 

5.进程池和线程池(重要掌握)

5.1 什么是池?

池是用来保证计算机硬件安全的情况下最大限度的利用计算机,它降低了程序运行的效率但是保证了计算机硬件的安全,从而让你的程序正常的运行!!

5.2 线程池的使用

# 1.导入进程池/线程池模块
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time

# 2.创建一个线程池对象,该对象括号内不传参数,默认是开设当前cpu个数*5的线程
pool = ThreadPoolExecutor(5) # 传入参数5,表示池子里固定创建了5个线程,这5个线程不会重复创建和销毁

def task(name):
    print('%s来了'% name)
    time.sleep(2)

# 3.朝池子中提交任务,第一个参数是函数名,第二个参数是函数需要传入的参数
"""
同步提交:提交任务之后原地等待返回结果,不往下执行任务
异步提交:提交任务之后不原地等待返回结果,继续往下执行
"""
# pool.submit(task,'zhang') # 注意:这里的提交是一个异步提交
# print('主')

# 优化
list = []
for i in range(20): # 向池子中提交20个任务。池子中只有5个线程
    res = pool.submit(task,i)  # 池方法异步提交之后有一个返回值,该返回值是一个future对象
    # print(res.result())  # 该对象有一个result方法,可以用来返回对应线程函数的返回值,没有返回值则返回none
    # result方法的使用使得submi异步提交变成了同步提交
    list.append(res)
# 如果想等待所有的线程全部执行完毕,在往下执行代码,可以使用池的shutdown方法
pool.shutdown() # shutdown的作用是等待所有线程池运行完毕,再关闭所有线程池往下运行
for i in list:
    print(i.result())

5.3 进程池的使用

基本同线程池,不同的是以下部分:

# 1.导入进程池/线程池模块
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time,os

pool = ProcessPoolExecutor(5)  # 不传默认是cpu的个数

def task(name):
    print('%s来了'% name)
    time.sleep(2)

# call_back函数里面需要传一个参数,该参数就是res(一个future对象)
def call_back(n):
    # 可以通过n.result()的方法获取task函数的返回值
    print(n.result())

if __name__ == '__main__':
    for i in range(20):
        # add_done_callback方法是给submit这个异步提交添加一个回调机制,完成任务之后立刻回调结果,方法的参数是一个函数
        res = pool.submit(task, i).add_done_callback(call_back)

协程

1.什么是协程

协程其实就是一种在单线程下实现并发的操作,程序猿通过检测自己写的代码,一旦遇到了IO操作的代码,我们就编写代码进行切换,这样使得cpu一直处于运行状态,提高程序的运行效率!

2.协程的具体写法

gevent模块的引用时为了使得函数之间不断的切换+保存状态!!

"""
使用协程,就必须要我们通过代码实时检测程序是否进入到了IO操作,如果进入了IO操作,
就进行快速的切换,以此来实现并发的效果,提升程序的运行效率
"""
# 1.导入gevent模块的spawn模块,该模块是用来监测函数里面的IO操作的,但是spawn模块是无法监测一些常见的IO操作的
# 因此,需要导入gevent模块中的monkey模块,以此来监测所有的IO操作
from gevent import monkey,spawn
monkey.patch_all()  # 猴子补丁
import time

def func1():
    print('hhh')
    time.sleep(2)
    print('lalala')

def func2():
    print('wwww')
    time.sleep(3)
    print('结束')

start_time = time.time()
g1 = spawn(func1) # spawn括号里是需要启动监测的函数名,函数名后面可以加需要传入的参数
g2 = spawn(func2) # 该方法是异步提交,有一个返回值,如果没有join方法会自动往下执行代码,结束程序
g2.join() # 等待函数执行完在往下执行
g1.join()
print(time.time()-start_time)

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:进程、线程补充与协程相关介绍 - Python技术站

(0)
上一篇 2023年4月2日 下午5:07
下一篇 2023年4月2日 下午5:07

相关文章

  • 序列化组件

    序列化组件的三大功能 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型 反序列化时同时会完成数据校验功能 序列化器Serializer使用方法 查询单个数据语法: 1.在setting.py中的app配置里注册一下drf 2.在dj…

    2023年4月2日
    00
  • 首页轮播图后端接口设计

    逻辑导航 1.当一访问127.0.0.1:8000时,就会向某一地址发送请求2.请求接口需要返回首页所需要的轮播图片3.前端vue轮播图组件循环一下后端发送的图片连接列表,依次展示轮播图 轮播图表设计 轮播图中一些共有的字段,我们可以创建一个公共的基础表,以后需要该字段直接基础该表就行 基表,可以放在自己写的utils.py里,以便后期使用! from dj…

    2023年4月2日
    00
  • 修改密码弹出框搭建

    前端代码搭建 主要利用的是bootstrap3中js插件里的模态框版块 <li><a href=”” data-toggle=”modal” data-target=”.bs-example-modal-lg”>修改密码</a></li> <div class=”modal fade bs-example…

    Python开发 2023年4月2日
    00
  • xadmin的使用

    安装 在项目的虚拟环境下执行 pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2 注意:xadmin对于不同django版本有不同的版本,一定要使用相对应的版本 在app中注册 INSTALLED_APPS = [ # … # xadmin主体模块 ‘xadmin’, # …

    Python开发 2023年4月2日
    00
  • rest_framework认证源码分析

    认证源码分析 位置 : APIVIew—-》dispatch方法—》self.initial(request, *args, **kwargs)—->有认证,权限,频率三个版块 分析: 只读认证源码: self.perform_authentication(request)—》self.perform_authentication(re…

    2023年4月2日
    00
  • 文章添加页(富文本编辑器的使用)

    1.文章添加页url开设 2.添加文章页面已经富文本编辑器的使用 富文本编辑器kindeditor只要到官网下载下来,放入static文件夹就行,如何在html的script处添加对应固定代码 {% extends ‘backend/base.html’ %} {% block article %} <h3>添加随笔</h3> &lt…

    2023年4月2日
    00
  • python基础(待补充)

    第一篇:计算机的基础知识 编程语言的介绍    计算机介绍和五大组成 平台与软件跨平台介绍 CS、BS架构和网络通信协议 操作系统的介绍 cpu详解 存储器详解 操作系统启动流程和BIOS介绍 关于编辑器和解释型编译型语言 第二篇:python环境的搭建 python介绍和解释器的安装(暂略) python程序的运行方式和步骤 集成开发环境和虚拟环境介绍 第…

    Python开发 2023年4月2日
    00
  • 操作系统的介绍

    操作系统的介绍 1.什么是操作系统 操作系统其实就是一个协调、管理、控制计算机硬件资源和软件资源的一个控制程序 2.为什么要有操作系统 (1)可以控制计算机硬件的基本运行(2)把操作硬件的复杂操作封装成一个简单的功能,交给上层的应用程序使用。例如文件就是操作系统提供给应用程序的一种功能! 3.程序的区分 计算机硬件是死的,计算机硬件的运行是受软件控制的、所以…

    2023年4月2日
    00
合作推广
合作推广
分享本页
返回顶部