服务端的并发处理-多线程多进程

服务端的并发处理-多线程多进程

在服务端进行挂起监听的时候,可能会遇到同时大量的用户进行连接和数据请求,那么单进程的可以使用多路复用的方式进行解决这个问题,这个技术后面再讲,现在最简单的解决方案就是使用多线程和多进程

多线程与多进程的选择这取决于到底是IO密集型还是CPU密集型,IO密集型需要使用多线程,cpu密集型进行多进程

这里显然就是io密集型的,但是为了练习,现在使用多线程和多进程都实验一把

from socket import *
from multiprocessing import Process


def main():
    # 创建对象
    server = socket(AF_INET, SOCK_STREAM)
    # 配置断开释放端口
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    # 挂起服务器
    server.bind(('', 8888))
    # 设置成监听模式
    server.listen()
    while True:
        # 创建新的连接对象
        new_server, client_info = server.accept()
        # 创建新的进程
        print(f'用户{client_info}已经连接')
        p = Process(target=get_data, args=(new_server,client_info))
        # 启动进程
        p.start()
        # 因为内存对应的new_server存在两个指向,所以外面的必须关闭,否则无法释放掉对象
        new_server.close()


def get_data(new_server,client_info):
    # 拿出数据
    data = new_server.recv(1024)
    while data:
        # 打印数据
        print(f'来自{client_info}的数据{data}')
        # 如果一次没有取完,继续取
        data = new_server.recv(1024)
    # 客户端断开连接后进行关闭处理
    new_server.close()


if __name__ == '__main__':
    main()

多进程与多线程的区别在于是否进行对象关闭

from socket import *
from threading import Thread


def main():
    # 创建对象
    server = socket(AF_INET, SOCK_STREAM)
    # 配置断开释放端口
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    # 挂起服务器
    server.bind(('', 8888))
    # 设置成监听模式
    server.listen()
    while True:
        # 创建新的连接对象
        new_server, client_info = server.accept()
        # 创建新的进程
        print(f'用户{client_info}已经连接')
        t = Thread(target=get_data, args=(new_server,client_info))
        # 启动线程
        t.start()
        # 因为多线程并不会复制进程的内存,所以new_server的指向不会出现两个

def get_data(new_server,client_info):
    # 拿出数据
    data = new_server.recv(1024)
    while data:
        # 打印数据
        print(f'来自{client_info}的数据{data}')
        # 如果一次没有取完,继续取
        data = new_server.recv(1024)
    # 客户端断开连接后进行关闭处理
    new_server.close()


if __name__ == '__main__':
    main()

多进程的内存复制实质

笼统的认为多进程在开新进程的时候,子进程会复制父进程的内存空间,从而具备整整个父进程所具有的变量

这只是非常笼统的认为,实际上计算机内存的存储方式分为两种一种就是栈,一种是堆

在计算中,如果内存地址中的数据没有变量指向它,则认为整个数据已经不需要了,系统就会对这个数据进行清空以释放内存

实际在我们父进程中创建的对象new_server已经被存在内存里面,父进程保存的是一个指向这个数据的映射关系,那么父进程在产生子进程的时候,子进程会复制父进程的这些映射关系,此时new_server对象映射的ip就有两个变量指向这个内存空间,从而使得内存中的这个数据ip就会受到两个指向

上面的进行多进程中,进程函数确实执行完成之后清空的子进程对这个数据ip的指向,子进程的映射关系呗删除,然而这个数据ip还存在一个父进程的变量进行映射,系统发现内存中这个数据ip居然还有一个指向,因此不会进行删除释放内存

那么回头看代码,在父进程分裂出子进程后直接继续执行,父进程马上就对自己的映射关系进行删除,此时整个内存里面的数据ip不会消失,因为子进程中还有对这个数据ip的指向

如果子进程也删除了自己的指向,系统就会知道这个数据ip已经没有任何指向了,那么系统就会清空这个数据ip,从而彻底结束这个new_server.

栈是一种先进后出的数据存储模式,我们大部分变量名就存在这里面,其优势在于非常快,然而变量名并不是变量值,栈中存储的都是一些映射方式

堆则不同,堆用来存大量的复杂数据,其容量大,但是速度慢

比如如果存在

a = 1

b = 1

那么1这个数据就有两个变量进行指向,所以数据1的映射数为2,当我们执行a += 1,此时变量名a指向的是2,而2是一个新的数据,在内存中会分配一个新的地址,此时数据1的映射数就减1称为了1.系统之所以没有清空数据1占用的内存,因为b这个变量映射在数据1上面还有映射

敲黑板

如果此时对b进行 b += 1的操作,则此时就会执行,先将数据2上面增加一个映射关系,这个映射是b变量指向的,此时数据2存在的映射数为2,那么原来的数据1就会减少一个映射关系,此时的数据1上的映射数为0,系统此时发现数据1上已经没有映射了,那么就会将数据0占用的内存释放掉,那么ab两个变量再次指向的是同一片内存空间

猜你喜欢

转载自blog.csdn.net/weixin_43959953/article/details/85008426
今日推荐