一. 同步互斥方法 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问题
-
效率测试
single cpu: 8.447520017623901
single IO: 5.697270393371582Thread cpu: 8.725268125534058
Thread IO: 5.536705255508423Process cpu: 4.151714563369751
Process IO: 3.824965000152588结论 : 在无阻塞情况下,多线程程序运行效率和单线程几乎相近,而多进程可以有效的提高程序的运行效率
-
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. 难易程度: 通信难度,逻辑处理难度等考量
要求 :
- 对进程线程怎么理解/说说进程线程差异特点
- 进程间通信都知道哪些,有什么特点
- 什么是同步互斥,说说你的使用情况
- 给一个情形分析用进程还是线程去实现,怎么实现
- 问一些概念,僵尸进程处理,GIL问题,进程状态问题
- 就项目问实现方法,为什么
四. 网络通信模型
-
通信模型分类
【1】循环网络模型:循环接收客户端请求,处理请求,同时只能处理一个客户端请求任务,处理完再进行下一个。
优点:实现简单,占用资源少 缺点:无法同时处理多个请求,效率不高 适用情况: 客户端不会长期占有服务器,任务比较小,任务量不大。udp比tcp实现更简单
【2】IO并发网络模型:利用IO多路复用等IO模型技术,同时处理多个IO任务请求。
优点:资源消耗少,能同时处理多个IO 缺点:只能处理IO操作
【3】多进程/线程并发:当一个客户端连接服务器,就创建一个新的进程/线程处理客户端请求,客户端退出时对应的进程/线程也随之销毁
优点: 同时满足多个客户端长期占有服务器需求,可以处理各种请求 缺点: 资源消耗较大 使用情况:客户端请求比较复杂,处理时间较长,配合较强的服务器部署技术(负载均衡,集群技术,分布式处理,缓存队列等)
-
多进程网络并发
基于fork的并发模型
- 创建监听套接字
- 循环等待客户端连接
- 有客户端连接创建新的进程处理客户端请求
- 原进程继续等待其他客户端连接
- 如果客户端退出,则销毁对应进程
五、FTP文件
- 功能
分为服务端和客户端,要求可以多个客户端同时操作
客户端可以查看服务器中有哪些文件(普通文件,不包含隐藏文件)
客户端可以从服务器中下载文件到本地目录下。
客户端可以将本地文件上传到服务端
在客户端终端打印简单的命令提示界面
客户端
'''
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()