PythonThread_08_bj

前情回顾

信号 :在进程间通过信号传递讯息
信号处理函数:
os.kill(pid,sig) 发送信号
signal.alarm(sec) 向自己发送时钟
signal.pause() 阻塞等待
signal.signal(sig,handler) 处理信号
handler(sig,frame)

信号量 semaphore
acquire() 删除信号量
release() 添加信号量
get_value() 获取信号量值

同步互斥机制 : 解决对共享资源的争夺

 event  事件
    e.wait()  e.set()  e.clear()

 lock 锁
    acquire() 上锁
		release() 解锁

threading 创建线程
Thread() 线程创建类
t.start()
t.join()


线程间通信

通信方法 : 多个线程共用进程空间,所以进程的全局            
           量对进程内的线程均可见。因此线程间没            
           有特有的通信方式往往使用全局变量通信
 
注意事项 :线程间使用全局变量通信往往需要同步             
           互斥机制做为通信的安全保证

#01_thread0.py
from threading import Thread
from time import sleep
#全局变量
l = []

def th1():
global l
l.append(100)

def th2():
sleep(1)
print(l)

t1 = Thread(target = th1)
t2 = Thread(target = th2)
t1.start()
t2.start()

sleep(0.5)
l = [10000]

t1.join()
t2.join()

#[10000]

线程的同步互斥

线程event对象

创建对象 :
e = threading.Event()
e.wait([timeout]) 如果e为设置状态则不会阻塞,未 设置则阻塞
e.set() 将e变为设置状态
e.clear() 将e的设置去除

#02_thread_event.py
import threading
from time import sleep

msg = None

#创建事件对象
e = threading.Event()

def bar():
print(“呼叫foo”)
global msg
msg = “天王盖地虎”

def foo():
print(“等待口令”)
sleep(2)
if msg == “天王盖地虎”:
print(“宝塔镇河妖,自己人,哈哈哈”)
else:
print(“口令错误,打死他”)
e.set()

def fun():
print(“呵呵…内奸出现”)
sleep(1)
e.wait()
global msg
msg = “小鸡炖蘑菇”

t1 = threading.Thread(target = bar)
t2 = threading.Thread(target = foo)
t3 = threading.Thread(target = fun)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()

呼叫foo

等待口令

呵呵…内奸出现

宝塔镇河妖,自己人,哈哈哈

线程锁
lock = threading.Lock() 创建锁对象
lock.acquire() 上锁
lock.release() 解锁

* 在一个进程中对一个线程锁重复上锁则会阻塞

#03_thread_lock.py
import threading

a = b = 0
lock = threading.Lock()

def value():
while True :
lock.acquire()
if a != b:
print(“a = %d,b = %d”%(a,b))
lock.release()

t = threading.Thread(target = value)
t.start()

while True:
lock.acquire()
a += 1
b += 1
lock.release()

t.join()

Python线程的GIL问题 (全局解释器锁)

python —》 支持多线程 --》同步互斥 --》加锁 —》超级锁,
给解释器加锁

后果 : 一个解释器,同一时刻只能解释一个线程。因此
大大降低了Python线程的执行效率

Python 的GIL问题 解决方法

  • 尽量使用进程方式并行
  • 不适用c作为解释器 c# java
  • Python线程适用于高延迟的IO操作,网络操作。不适合
    用cpu密集型程序。线程在遇到IO阻塞时会让出解释器和cpu

效率测试

分别测试 在 IO密集型程序和CPU密集型程序下,多进程,多线程,
单进程执行效率

#test.py
#计算密集

def count(x,y):
c = 0
while c < 6000000:
c += 1
x += 1
y += 1

#IO密集
def write():
f = open(‘test.txt’,‘w’)
for x in range(1000000):
f.write(“比利时加油\n”)
f.close()

def read():
f = open(‘test.txt’)
lines = f.readlines()
f.close()

Line cpu: 8.061699390411377
Line IO: 5.261598348617554

Thread cpu: 8.920610427856445
Thread io: 5.60230565071106

process cpu: 4.07219386100769
Process io: 3.383375406265259

进程和线程的区别和联系
1.两者都是多任务编程的方式,都能够使用计算机多核
2.进程的创建和删除要比线程消耗更多计算机资源
3.进程空间独立,数据安全性好,有专门的进程间通信方法
4.线程使用全局变量通信,更加简单,但是往往要与同步互斥机制公用
5.一个进程可以包含多个线程,线程共享进程的资源空间
6.进线程都有自己特有的资源,如命令,属性 id等

使用场景 :

  • 需要创建较多的并发,任务比较简单时,线程比较合适
  • 如果程序数据资源比较复杂,特别是共享资源较多时,
    需要考虑到线程锁的复杂性
  • 如果多个任务无必要的关联性,不易将其强行融入到一个进程中
  • Python线程不适合cpu密集型程序

总结 :

  1. 进程线程的区别和关系
  2. 进程间以什么方式通信,有什么特点
  3. 同步互斥的意义,有什么方法实现同步互斥
  4. 僵尸进程,进程状态,线程GIL的概念
  5. 给一个情景,问选择进程还是线程,怎么做为什么

服务器模型

硬件服务器 : 主机 集群
厂商 : IBM HP 联想 浪潮

软件服务器 : 编写的服务端程序,依托于硬件服务器
运行,提供给用户一定的软件服务

分类 :webserver ----》 网络后端程序提供网络请 求的后端处理和数据交互
httpserver ----》 处理HTTP请求,回复http响 应
邮箱服务器 -----》 提供邮箱服务
文件服务器 -----》 提供文件的上传下载

功能: 网络连接,逻辑处理,数据的交互,数据的传输
协议的实现

模型结构 : c/s (客户端服务器模型)
b/s (浏览器服务器模型)

服务器目标:处理速度快,数据更安全,并发量大

硬件 : 更高的配置,集成分布的技术,更好的网络速
度,更多的主机,网络安全

软件 : 程序占有更少的计算机资源,更稳定的运行效
果,更流畅的运行速度,采用更合理的技术。
处理更高的并发

服务器模型

循环服务器 :单进程程序,循环的接收客户端的请求,
处理请求,每处理完一个请求后再去接收
处理下一个请求

优点 : 实现简单,占用系统资源少
缺点 : 无法同时连接多个客户端,当一个客户端长
期占有服务器时会形成其他客户端无法访问
的情况
使用情况:任务比较短暂,udp套接字更适合循环

并发服务器 : 同时能够处理多个客户端的任务请求。

          并发可分为IO并发或者多进程多线程并发。

	IO并发 : IO多路复用    协程

		 优点:资源消耗少,适用于IO类型服务器
		 缺点:不能监控CPU密集的情况,单线程,不能       
              长期阻塞的消息的收发

	进程/线程并发:为每个客户端单独提供一个进程线                 程,处理客户端请求

   优点: 客户端可以长期占用服务器
   缺点: 消耗系统资源较多

多进程并发模型

使用fork完成并发

1.创建套接字 绑定 监听
2.等待接收客户端请求 accept
3.创建子进程处理客户端请求,
父进程继续准备接收其他客户端连接
4.客户端退出则子进程结束

#04_fork_server.py
#fork tcp 并发
from socket import *
import os,sys
import signal

#创建服务器地址
HOST = ‘172.60.50.50’
PORT = 8888
ADDR = (HOST,PORT)

#处理客户端请求函数
def client_handle©:
print(“子进程处理客户端:”,c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b’Receive your message’)

#创建tcp监听套接字
s = socket()
#设置端口重用
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#绑定IP和端口号
s.bind(ADDR)
#设置监听
s.listen(5)

#循环:父进程继续准备接收其他客户端连接

#处理僵尸进程
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
#打印端口监听情况
print(“Listen to 8888…”)

#循环接收客户端连接
while True:
try:
c,addr = s.accept()
#异常:用户中断执行(通常是输入ctrl+C)
except KeyboardInterrupt:
s.close()
sys.exit(“服务器退出”)
#其它异常,打印一下异常信息
except Exception as e:
print(e)
continue

#创建子进程处理客户端请求

#有客户端连接,创建子进程
pid = os.fork()
if pid < 0:
    print("创建子进程失败")
elif pid == 0:
    s.close()  #子进程不需要监听套接字
    #print("处理客户端请求") #转换成下列函数处理
	#调用函数处理客户端请求
    client_handle(c)
    #子进程处理完客户端请求一定要退出
    c.close()
    sys.exit(0)
else:
    c.close() #父进程不需要和客户端交互
    continue

#tcp_client.py
from socket import *

#创建套接字
sockfd = socket(AF_INET,SOCK_STREAM)

#发起连接 地址:服务端地址
sockfd.connect((‘172.60.50.50’,8888))

while True:
data = input(‘发送>>’)
if not data:
break
#将内容变为bytes格式发送
sockfd.send(data.encode())
data = sockfd.recv(1024).decode()
print(“收到消息:”,data)

sockfd.close()

tftp文件服务器

项目功能 :

  • 客户端有简单的页面命令提示
  • 功能包含 1. 查看服务端文件库文件列表
    2. 下载其中的某个文件到本地
    3. 将本地文件上传到服务器文件库中
    4. 退出
  • 服务端需要:1.处理客户端的各种请求
    2.允许多个客户端同时进行操作

技术分析 :tcp套接字
并发 —》 多进程
对文件的读写发送操作
查看文件列表时需要考虑到粘包问题

整体结构 :功能封装在类中(上传,下载,查看列表)

       流程控制使用main()
	   创建套接字,创建连接创建进程,接收请求

作业 : 完成 get 和 put功能

#tftp_server.py

‘’’
tftp 文件服务器程序
‘’’
from socket import *
import os
import signal
import sys
import time

#文件库路径
FILE_PATH = “/home/tarena/”

#实现服务器功能模块
class TftpServer(object):
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
else:
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 = files + file + '#'
    self.connfd.send(files.encode())     


def do_get(self,filename):
    try:
        fd = open(FILE_PATH + filename,'rb')
    except:
        self.connfd.send("文件不存在".encode())
        return
    self.connfd.send(b"OK")
    time.sleep(0.1) 
    #发送文件
    try:
        for line in fd:
            self.connfd.send(line)
        fd.close()
    except Exception as e:
        print(e)
    time.sleep(0.1)
    self.connfd.send(b'##')
    print("文件发送完毕")


def do_put(self,filename):
    try:
        fd = open(FILE_PATH + filename,'w')
    except:
        self.connfd.send("无法完成上传")
    self.connfd.send(b'OK')
    while True:
        data = self.connfd.recv(1024).decode()
        if data == "##":
            break
        fd.write(data)
    fd.close()
    print("上传完毕")

#流程控制,创建套接字连接,接收请求
def main():
#创建服务器地址
HOST = ‘0.0.0.0’
PORT = 8888
ADDR = (HOST,PORT)

#创建套接字
sockfd = socket()
#设置端口可重用
sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
#绑定IP和端口号
sockfd.bind(ADDR)
#设置监听
sockfd.listen(5)

#循环:父进程继续准备接收其他客户端连接

#处理僵尸进程	
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
#打印端口监听情况
print("Listen to port 8888....")

#循环接收客户端连接
while True:
    try: 
        connfd,addr = sockfd.accept()
    except KeyboardInterrupt:
        sockfd.close()
        sys.exit("服务器退出")
    except Exception as e:
        print(e)
        continue
	#打印客户端地址
    print("客户端登录:",addr)

    #创建子进程处理客户端请求

    #有客户端连接,创建子进程
    pid = os.fork()
    if pid < 0:
        print("创建子进程失败")
        continue
    elif pid == 0:
        sockfd.close()
        tftp = TftpServer(connfd)
        #接收客户端请求
        while True:
            data = connfd.recv(1024).decode()
            if not data:
                continue
            elif data[0] == 'L':
                tftp.do_list()
            #data ==> G filename
            elif data[0] == 'G':
                filename = data.split(' ')[-1]
                tftp.do_get(filename)
            elif data[0] == 'P':
                filename = data.split(' ')[-1]
                tftp.do_put(filename)
            elif data[0] == 'Q':
                print("客户端退出")
                sys.exit(0)

    else:
        connfd.close()
        continue

if name == “main”:
main()

#tftp_client.py

from socket import *
import sys
import time

#实现基本的请求功能
class TftpServer(object):
def init(self,sockfd):
self.sockfd = sockfd

def do_list(self):
    self.sockfd.send(b"L") #发送请求类型
    #等待接收服务器端确认
    data = self.sockfd.recv(1024).decode()
    if data == 'OK':
        data = self.sockfd.recv(4096).decode()
        files = data.split('#')
        for file in files:
            print(file)
        print("%%%%%文件列表展示完毕%%%%%\n")
    else:
        #失败的原因由服务器发送过来
        print(data)


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

def do_put(self,filename):
    try:
        fd = open(filename,'rb')
    except:
        print("上传的文件不存在")
        return
    self.sockfd.send(("P " + filename).encode())
    data = self.sockfd.recv(1024).decode()
    if data == 'OK':
        for line in fd:
            self.sockfd.send(line)
        fd.close()
        time.sleep(0.1)
        self.sockfd.send(b'##')
        print("%s 文件上传完毕"%filename)
    else:
        print(data)


def do_quit(self):
    self.sockfd.send(b'Q')

#套接字连接
def main():
# 测一下列表的长度,看IP和端口是否忘输入
if len(sys.argv) < 3:
print(“argv is error”)
return
#创建服务器地址
HOST = sys.argv[1]
PORT = int(sys.argv[2])
#要访问的服务端地址
ADDR = (HOST,PORT)

#创建套接字
sockfd = socket()
#连接
sockfd.connect(ADDR)

tftp = TftpServer(sockfd) #tftp对象调用请求方法

while True:
    print("=======命令选项========")
    print("******* list *********")
    print("*******get file ******")
    print("*******put file ******")
    print("******* quit *********")
    print("======================")

    cmd = input("请输入命令>>")

    if cmd.strip() == 'list':
        tftp.do_list()
    elif cmd[:3] == "get":
        filename = cmd.split(' ')[-1]
        tftp.do_get(filename)
    elif cmd[:3] == "put":
        filename = cmd.split(' ')[-1]
        tftp.do_put(filename)
    elif cmd.strip() == "quit":
        tftp.do_quit()
        sockfd.close()
        sys.exit("欢迎使用") 
    else:
        print("请输入正确的命令!!!")
        continue

if name == “main”:
main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值