5,线程池,进程池,协程,IO模型

好久不见. 提交于 2019-11-27 11:03:39
今日内容:    1,线程池    2,进程池    3,协程    4,IO 模型服务端要满足这三个条件:    1,24小时不间断的提供服务    2,能够支持高并发    3,要有固定的IP地址和端口在服务端这个地方会出现阻塞态情况:    阻塞IO 操作有:    1,链接循环    2,通信循环单线程实现高并发思路:        为了更好的提高程序的运行效率,即实现高并发,让服务端同时能够接受多个客户端的消息        所以一般在服务端会把,连接循环和通信循环封装为两个不同的函数方法,        这样当一个客户端与服务端进行通信时,服务端的连接循环可以和其他客户端进行连接,        不要等待,从而提高了效率,可以利用单线程来完成高并发,即来一个客户端就开一个线程,        每来一个客户端就开一个线程,看上去像高并发!    案列:        import socket        from threading import Thread        def server1():            server = socket.socket()            server.bind(('127.0.0.1', 1688))            server.listen(5)            server2(server)        def task(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 server2(server):   连接循环            while True:                conn, addr = server.accept()      # 在这里会形成阻塞,监听是否有客户端过来                t = Thread(target=task, args=(conn,))                t.start()    问题:上面的案例虽然解决了线程高并发的问题,但是但有大量的客户端来访问服务端时,由于开启线程也是需要消耗资源的            所以但有几亿个用户同时访问时,也架不住量大,也会造成内存溢出,服务器奔溃现象,这是由于硬件性能决定的,            所以为了防止服务器奔溃,保证数据的安全性,在保证计算机能够承受的范围内,最大限度的利用计算机,            就引入了池这个概念            1,什么是池?                在保证计算机硬件安全的情况下最大限度的利用计算机               池其实是降低了程序的运行效率 但是保证了计算机硬件的安全               (硬件的发展跟不上软件的速度)           2,线程池,               进程池               案列:                import time                from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor                # 线程池,在你不传参数的情况下,默认的是当前计算机CPU的个数*5                pool = ThreadPoolExecutor(5)   # 括号内可以传参数,指定线程池的线程个数                # 只要创建了五个线程,就永远是这五个线程,这五个线程并不会动态创建,动态销毁                # 好处是节省空间,进程也是如此                def task(n):                    print(n)                    time.sleep(2)                    #return n*2                t_list = []                for i in range(20):                    res = pool.submit(task,i)  # submit 的返回值是一个内部Future类产生的对象                    print(res)   # 类对象    res.result()它拿到的结果就是task任务的返回值                    print(res.result())  # 结果为 0 None 1 None 2 None 3 None...19 None                        1,将结果变为串行  2,结果为None (在这里res.result()拿到得是task这个任务的返回结果                        原地等待任务的返回结果 ,即同步,如果这样调就会将并发变为串行                        结果为None,是因为task任务的返回结果为None,所以为空,如果加上return n*2,那就不为空                    t_list.append(res)                pool.shutdown() # 1,关闭池子,即20个线程提交完毕,立马关闭池子,就像关门打狗一样                                # 2,等待池子里面所有的任务完成之后才会继续向下执行                for p in t_list:                    print('>>>>>对象',p.result(),p)                    # 结果是乱的:14                    #             >>>>>对象 None <Future at 0x2aa9829a5f8 state=finished returned NoneType>                    如果加上pool.shutdown(),就是等任务全部结束,才会走pool.shutdown() 下面的代码,

 

 

            3,进程池                from concurrent.futures import ProcessPoolExecutor                pool = ProcessPoolExecutor(5)   #进程池不传参数,默认当前CPU的个数                            #异步回调机制:当异步提交的任务有返回结果之后,会自动触发回调函数的执行

 

            4,协程:                1,什么是协程:单线程下实现并发  只是一个名称,即表示一个效果                        进程:正在运行的程序,资源单位                        线程:cpu最小的执行单位                        并发:看起来像同时进行,就可以称之为并发,即切换+保存状态                        并行:真正的同时进行,单核情况下无法并行                    多道技术:1,空间上的复用  即 多个程序利用一台计算机                              2,时间上的复用  切换+保存状态                        切换的状态:1,程序遇到IO操作,就会进行切换                                    2,长时间的占用CPU 时,时间片用完,也会切换                        程序员自己通过代码自己检测程序中的IO                        一旦遇到IO自己通过代码切换                        给操作系统的感觉是你这个线程没有任何的IO                        ps:欺骗操作系统 让它误认为你这个程序一直没有IO                            从而保证程序在运行态和就绪态来回切换                            提升代码的运行效率                gevent模块                    import time                    from gevent import monkey;monkey.patch_all() #固定写法                    from gevent import spawn                    # 注意gevent模块没办法自动识别time.sleep()等io情况                    # 需要你手动在配置一个参数,自动检测io,遇到io就开始切换                    def heng():                        print('哼22')                        time.sleep(1)                        print('哈哈哈我')                                        def ao():                        print('嗷嗷')                        time.sleep(3)                        print('收拾一下')                                        start = time.time()  # 在这里注意spawn()是有返回值的,返回值是一个类的对象                    g1 = spawn(heng)     # spawn ,相当于一个列表,先来一个heng添加到列表中,后面再来一个ao在添加到列表中                    g2 = spawn(ao)        # 一旦heng遇到IO 就执行ao,一旦ao遇到IO就立即执行heng,就是不断来回切换                    g1.join()      # 等待任务完成,不然就不会执行此任务,而是走主线程,主线程没代码,程序直接结束                    g2.join()            # 在右键运行后,spawn就在这两个任务之间来回疯狂的切,这个切换是在代码层面的切                                            # 来欺骗操作系统,让操作系统误认为没有遇到IO                    # heng()   # 串行执行                    # ao()     # 串行执行    这样执行的结果是5秒多                    print(time.time()- start)       # spawn(heng) spawn(ao)执行的结果        5,IO模型                数据交互,要的数据都在内存,是互相把数据发到对方的内存                1,阻塞IO                    先系统要数据,没有数据时,阻塞+等待数据,一旦有数据来了                    进行数据的拷贝,拷贝到数据在回复ok,中间等待和拷贝的过程都是阻塞IO                2, 非阻塞IO                    程序处于运行态和就绪态,向内存要数据,没有数据也会立马给你返回一个结果                    一直反复的要,一直反复的回,直到有数据真的来了,就会进行拷贝,此时会形成阻塞                    这就是非阻塞io                3, 异步IO                    异步进行,异步提交任务后,立即给你一消息,但是内部在等待,等到内部有数据是立马                    返回给你,进行下一步操作                4, IO多路复用                    就是 select,相当于一个检测机制,像一个列表,统一管理,要什么数据select去负责跟                    内存要数据,由他负责,不用你管,有了之后回来给你,然后再执行以下操作,相当于餐厅服务员
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!