GIL问题--进程线程区别--通信模型--FTP代码

一. 同步互斥方法 Lock

from  threading  import Lock

lock = Lock()    创建锁
lock.acquire()   上锁  如果lock已经上锁会阻塞
lock.release()   解锁 
	
with  lock:   上锁
	...
	...  
		with 代码块结束自动解锁
from threading import Lock,Thread

a = b = 0
lock = Lock()   #创建锁
def value():
    while True:
        lock.acquire()
        if a != b:
            print("a = %d,b = %d"%(a,b))
        lock.release()
t = Thread(target=value)
t.start()
while True:
    with lock:
        a +=1
        b += 1
t.join()

二. Python线程的GIL问题

  1. 效率测试

    single cpu: 8.447520017623901
    single IO: 5.697270393371582

    Thread cpu: 8.725268125534058
    Thread IO: 5.536705255508423

    Process cpu: 4.151714563369751
    Process IO: 3.824965000152588

    结论 : 在无阻塞情况下,多线程程序运行效率和单线程几乎相近,而多进程可以有效的提高程序的运行效率

  2. python GIL问题 (全局解释器锁)

    【1】 什么是GIL :由于python解释器设计中加入了全局解释器锁,导致python解释器同时只能解释一个线程,大大降低了python的执行效率。
    【2】 导致后果: 因为遇到阻塞python线程会主动让出解释器,所以python线程在高延迟,多阻塞的情况下可以提高运行效率,其他情况并不适合。
    【3】 GIL问题建议
    * 修改c解释器
    * 尽量使用进程并发
    * 不使用c作为解释器 (java c#)

三. 进程线程的区别联系

  • 【1】 区别联系
    1. 两者都是多任务编程方式,都能使用计算机多核资源
    2. 进程的创建和删除消耗的计算机资源比线程多
    3. 进程空间独立,数据互不干扰,有专门通信方法。线程使用全局变量通信
    4. 一个进程可以包含多个线程,两者存在包含关系
    5. 多线程共享进程资源,对共享资源操作时往往需要同步互斥处理
    6. 进程线程都是运行过程描述,有自己的属性标志

  • 【2】 使用场景

      1. 任务场景: 如果是相对独立的任务模块可能使用进程,如果是多个分支共同构成一个完整功能可能用线程
      2. 项目结构:多种语言实现不同任务模块可能是多进程。
      3. 语言特点:比如Java一些语言实现线程资源少效率高,Python有GIL 等
      4. 难易程度: 通信难度,逻辑处理难度等考量
    

要求 :

  1. 对进程线程怎么理解/说说进程线程差异特点
  2. 进程间通信都知道哪些,有什么特点
  3. 什么是同步互斥,说说你的使用情况
  4. 给一个情形分析用进程还是线程去实现,怎么实现
  5. 问一些概念,僵尸进程处理,GIL问题,进程状态问题
  6. 就项目问实现方法,为什么

四. 网络通信模型

  1. 通信模型分类

    【1】循环网络模型:循环接收客户端请求,处理请求,同时只能处理一个客户端请求任务,处理完再进行下一个。

     优点:实现简单,占用资源少
     缺点:无法同时处理多个请求,效率不高
    
     适用情况: 客户端不会长期占有服务器,任务比较小,任务量不大。udp比tcp实现更简单
    

    【2】IO并发网络模型:利用IO多路复用等IO模型技术,同时处理多个IO任务请求。

     优点:资源消耗少,能同时处理多个IO
     缺点:只能处理IO操作
    

    【3】多进程/线程并发:当一个客户端连接服务器,就创建一个新的进程/线程处理客户端请求,客户端退出时对应的进程/线程也随之销毁

     优点: 同时满足多个客户端长期占有服务器需求,可以处理各种请求
     缺点: 资源消耗较大
    
     使用情况:客户端请求比较复杂,处理时间较长,配合较强的服务器部署技术(负载均衡,集群技术,分布式处理,缓存队列等)
    
  2. 多进程网络并发

    基于fork的并发模型

    • 创建监听套接字
    • 循环等待客户端连接
    • 有客户端连接创建新的进程处理客户端请求
    • 原进程继续等待其他客户端连接
    • 如果客户端退出,则销毁对应进程

五、FTP文件

  1. 功能
    分为服务端和客户端,要求可以多个客户端同时操作
    客户端可以查看服务器中有哪些文件(普通文件,不包含隐藏文件)
    客户端可以从服务器中下载文件到本地目录下。
    客户端可以将本地文件上传到服务端
    在客户端终端打印简单的命令提示界面

客户端

'''
ftp服务器
fork并发
'''
from socket import *
import os,sys
import signal
import time

# 全局变量
ADDR = ("0.0.0.0",9999)
FILE_PATH = "/home/tarena/test/"
# 服务端功能类
class FtpServer():
    def __init__(self,connfd):
        self.connfd = connfd

    def do_list(self):
        # 获取文件列表
        file_list = os.listdir(FILE_PATH)
        if not file_list:
            self.connfd.send("文件库为空".encode())
            return
        self.connfd.send(b"OK")
        time.sleep(0.1)
        files = ""
        for file in file_list:
            if file[0] != "." and os.path.isfile(FILE_PATH+file):
                files += file + "#"
        self.connfd.send(files.encode())

    def do_get(self,filename):
        try:
            fd = open(FILE_PATH + filename,"rb")
        except IOError:
            self.connfd.send("文件不存在".encode())
            return
        else:
            self.connfd.send(b"OK")
            time.sleep(0.1)
        #发送文件内容
        while True:
            data = fd.read(1024)
            if not data:
                time.sleep(0.1)
                self.connfd.send(b"##")
                break
            self.connfd.send(data)
        fd.close()

    def do_put(self, filename):
        self.connfd.send(b"OK")
        filename = FILE_PATH+filename   #本地服务器的文件路径
        fd = open(filename,"wb")
        while True:
            data = self.connfd.recv(1024)
            if data ==b"##":
                break
            fd.write(data)
        fd.close()


def do_requests(connfd):
    ftp = FtpServer(connfd)
    while True:
        data = connfd.recv(1024).decode()
        if not data or data[0] == "Q":
            connfd.close()
            return
        elif data[0] == "L":
            ftp.do_list()
        elif data[0] == "G":
            filename = data.split(" ")[-1]
            ftp.do_get(filename)
        elif data[0] == "P":
            filename = data.split(" ")[-1]
            filename = filename.split("/")[-1]
            ftp.do_put(filename)



# 网络搭建
def main():
    # 创建套接字
    sockfd = socket()
    sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sockfd.bind(ADDR)
    sockfd.listen(5)
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)
    print("等待连接")
    while True:
        try:
            connfd,addr = sockfd.accept()
        except KeyboardInterrupt:
            sockfd.close()
            sys.exit("服务器退出")
        except Exception:
            continue
        print("连接:",addr)
        pid = os.fork()
        if pid == 0:
            sockfd.close()
            do_requests(connfd)
            os._exit(0)
        else:
            connfd.close()
if __name__ == "__main__":
    main()

服务端

from socket import socket
import sys
import time
# 服务器地址
ADDR = ("127.0.0.1",9999)

# 客户端功能类
class FtpClient:
    def __init__(self,sockfd):
        self.sockfd = sockfd

    def do_list(self):
        self.sockfd.send(b"L")  #发送请求
        #等待回复
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            files = self.sockfd.recv(4096).decode()
            for file in files.split("#"):
                print(file)
        else:
            #无法完成操作
            print(data)

    def do_quit(self):
        self.sockfd.send(b"Q")
        self.sockfd.close()
        sys.exit("谢谢使用")

    def do_get(self,filename):
        self.sockfd.send(("G " + filename).encode())
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            fd = open(filename,"wb")
            while True:
                data = self.sockfd.recv(1024)
                # print(data.decode())
                if data == b"##":
                    break
                fd.write(data)
            print("下载完成")
            fd.close()
        else:
            print(data)

    def do_put(self, filename):
        # filename = filename.split("/")[-1]
        self.sockfd.send(("P " + filename).encode())    #发送P + 整个文件路径到服务端
        data = self.sockfd.recv(128).decode()
        if data == "OK":
            try:
                fd = open(filename,"rb")
            except IOError:
                print("文件名或路径错误")
                return
            while True:
                data = fd.read(1024)
                if not data:
                    # time.sleep(0.1)
                    self.sockfd.send(b"##")
                    break
                self.sockfd.send(data)
            print("上传完成")
            fd.close()






def main():
    sockfd = socket()
    try:
        sockfd.connect(ADDR)
    except Exception as e:
        print(e)
        return
    # 创建对象调用功能函数
    ftp = FtpClient(sockfd)

    while True:
        print("\n============命令选项============")
        print("*****  1.    列表      **********")
        print("*****  2.  下载文件    **********")
        print("*****  3.  上传文件     **********")
        print("*****  4.    退出      **********")
        print("================================")

        cmd = input(">>>:")
        if cmd.strip() == "list":
            ftp.do_list()
        elif cmd.strip() == "quit":
            ftp.do_quit()
        elif cmd.strip()[:3] == "get":
            filename = cmd.split(" ")[-1]
            ftp.do_get(filename)
        elif cmd.strip()[:3] == "put":
            filename = cmd.split(" ")[-1]
            ftp.do_put(filename)
        else:
            print("请输入正确指令")


if __name__ == "__main__":
    main()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值