灵虚御风
醉饮千觞不知愁,忘川来生空余恨!

导航

 

 

昨日内容回顾
    TCP服务端实现并发
        1.将不同的功能尽量拆分成不同的函数
            拆分出来的功能可以被多个地方使用
        1.将连接循环和通信循环拆分成不同的函数
        2.将通信循环做成多线程

    GIL(全局解释器锁)
    在CPython解释器才有GIL的概念,不是python的特点
    GIL也是一把互斥锁
        将并发变成串行 牺牲了效率但是提高了数据的安全
        ps:
            1.针对不同的数据 应该使用不同的锁去处理
            2.自己不要轻易的处理锁的问题 哪怕你知道acquire和release
            当业务逻辑稍微复杂的一点情况下 极容易造成死锁
    CPython中的GIL的存在是因为python的内存管理不是线程安全的

    内存管理
        引用计数:值与变量的绑定关系的个数
        标记清除:当内存快要满的时候 会自动停止程序的运行 检测所有的变量与值的绑定关系
                给没有绑定关系的值打上标记,最后一次性清除
        分代回收:(垃圾回收机制也是需要消耗资源的,而正常一个程序的运行内部会使用到很多变量与值
        并且有一部分类似于常量,减少垃圾回收消耗的时间,应该对变量与值的绑定关系做一个分类
        )        新生代(5S)》》》青春代(10s)》》》老年代(20s)
                垃圾回收机制扫描一定次数发现关系还在,会将该对关系移至下一代
                随着代数的递增 扫描频率是降低的


    同一个进程下的多个线程能否同时运行
    GIL类似于是加在解释器上面的一把锁


    死锁与递归锁
        递归锁
            可以被第一个抢到该锁的人多次的acquire和release,内部会有一个计数
            acquire就是加
            release就是减
            当别人在抢这把锁的时候  只要计数不为零 永远也别想抢到


    信号量
        如果互斥锁是单个卫生间
        那么信号量就是多个卫生间

    event事件
        e = Event()

        e.wait()  等待信号

        e.set()  发送信号

    线程q
        队列  Queue
        堆栈  LifoQueue
        优先级  P...Queue
            只有一个参数  是元组的形式

            数字越小优先级越高

进程池与线程池

开进程开线程都需要消耗资源,只不过两者比较的情况线程消耗的资源比较少

在计算机能够承受范围之内最大限度的利用计算机


什么是池?
    在保证计算机硬件安全的情况下最大限度的利用计算机
    池其实是降低了程序的运行效率 但是保证了计算机硬件的安全
    (硬件的发展跟不上软件的速度)

线程池进程池


协程

    进程:资源单位
    线程:执行单位
    协程:单线程下实现并发

    并发
        切换+保存状态
        ps:看起来像同时执行的 就可以称之为并发

    协程:完全是程序员自己意淫出来的名词
        单线程下实现并发

    并发的条件?
        多道技术
            空间上的复用
            时间上的复用
                切换+保存状态

    程序员自己通过代码自己检测程序中的IO
    一旦遇到IO自己通过代码切换
    给操作系统的感觉是你这个线程没有任何的IO
    ps:欺骗操作系统 让它误认为你这个程序一直没有IO
        从而保证程序在运行态和就绪态来回切换
        提升代码的运行效率


        切换+保存状态就一定能够提升效率吗???
            当你的任务是iO密集型的情况下  提升效率
            如果你的任务是计算密集型的   降低效率






    yield 保存上一次的结果


    多进程下开多线程
    多线程下再开协程




IO模型(了解)
进程池,线程池,IO模型总结

 

TCP 单线程实现并发

import socket
from threading import Thread,current_thread

def client():
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    n = 0
    while True:
        data = f'{current_thread().name}{n}'
        client.send(data.encode('utf-8'))
        res = client.recv(1024)
        print(res.decode('utf-8'))
        n += 1

for i in range(400):
    t = Thread(target=client)
    t.start()
client.py
from gevent import monkey;monkey.patch_all()
import socket
from gevent import spawn

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

def talk(conn):
    while True:
        try:
            data = conn.recv(1024)
            if len(data) == 0:break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

def server1():
    while True:
        conn,addr = server.accept()
        spawn(talk,conn)

if __name__ == '__main__':
    g1 = spawn(server1)
    g1.join()
server.py

 

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import time
import os

pool = ThreadPoolExecutor(5) # # 括号内可以传参数指定线程池内的线程个数, 也可以不传
# 不传,默认是当前所在计算机的cpu个数乘5
# pool = ProcessPoolExecutor() # 默认是当前计算机cpu的个数

"""
池子中创建的进程/线程创建一次就不会再创建了
至始至终用的都是最初的那几个
这样的话节省了反复开辟进程/线程的资源

"""
def task(n):
    print(n,os.getpid()) # 查看当前进程
    time.sleep(2)
    return n ** 2

def call_back(n):
    print('拿到了异步提交任务的返回结果:',n.result())

"""
提交任务的方式
    同步:提交任务之后 原地等待任务的返回结果 期间不做任何事
    异步:提交任务之后 不等待任务的返回结果(异步的结果怎么拿???) 直接执行下一行代码
"""
# pool .submit(task,1) # 朝线程池中提交任务  异步提交
# print('主')
"""
异步回调机制:当异步提交的任务有返回结果之后,会自动触发回调函数的执行

"""
if __name__ == '__main__':
    t_list = []
    for i in range(20):
        res = pool.submit(task,i).add_done_callback(call_back)
        # 提交任务的时候 绑定一个回调函数 一旦该任务有结果 立刻执行对于的回调函数
        # print(res.result()) # 原地等待返回的结果
        t_list.append(res)

    # pool.shutdown()     # 关闭池子 等待池子中所有的任务执行完毕之后 才会往下运行代码
    for p in t_list:
        print(">>>:",p.result())
1.进程池与线程池.py
""""""
"""串行执行"""
import time
def func1():
    for i in range(10000):
        i + 1
def func2():
    for i in range(10000):
        i + 1

start = time.time()
func1()
func2()
stop = time.time()
print(stop-start) # 0.0009989738464355469

"""基于yield 并发执行"""
def func3():
    while True:
        10000000 + 1
        yield

def func4():
    g = func3()
    for i in range(10000):
        time.sleep(100) # 模拟IO,yield并不会捕捉到并自动切换
        i + 1
        next(g)
start=time.time()
func4()
stop=time.time()
print(stop-start)

"""
需要找到一个能够识别IO的一个工具


gevent模块
"""
from gevent import monkey;monkey.patch_all()  # # 由于该模块经常被使用 所以建议写成一行
from gevent import spawn
import time
"""
注意gevent模块没办法自动识别time.sleep等io情况
需要你手动再配置一个参数

"""
def heng():
    print('')
    time.sleep(2)
    print('')

def ha():
    print('')
    time.sleep(3)
    print('')

def heiheihei():
    print('嘿嘿嘿')
    time.sleep(5)
    print('嘿嘿嘿')

start = time.time()
g1 = spawn(heng)
g2 = spawn(ha)   # spawn会检测所有的任务

g3 = spawn(heiheihei)
g1.join()
g2.join()
g3.join()
# heng()
# ha()
print(time.time() - start)
2.协程.py

 

posted on 2022-03-28 16:11  没有如果,只看将来  阅读(21)  评论(0编辑  收藏  举报