线程通信
通信方法 : 多个线程共用进程空间,所以进程的全局变量对进程内的线程均可见。因此使用全局变量通信是线程主要通信方法
注意事项 :线程间通信更容易产生资源争夺,往往需要同步互斥机制做为通信的安全保证
线程的同步互斥
线程的Event事件
操作:
e = threading.Event()
e.wait([timeout]) 如果e为设置状态则不阻塞,未设置则阻塞
e.set() 将e变为设置状态
e.clear() 将e设置去除
import threading
from time import sleep
s = None
# 创建事件对象
e = threading.Event()
def bar():
print("呼叫foo")
global s
s = "天王盖地虎"
def foo():
print("等待口令")
sleep(2)
if s == "天王盖地虎":
print("自己人")
else:
print("打死他")
e.set() # 将e变成设置状态
def fun():
print("呵呵....")
sleep(1)
e.wait() #设置阻塞
global s
s = "小鸡炖蘑菇"
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()
线程锁
lock = threading.Lock() 创建锁
lock.acquire() 上锁
lock.release() 解锁
操作原理:重复上锁,
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作为解释器
* python线程适用于高延迟的IO操作,网络操作,不适合CPU密集型或者传输速度很快的IO操作
注意:线程遇到阻塞会让出解释器
效率测试
Line cpu: 8.061699390411377
Line IO: 5.261598348617554
Thread cpu: 8.920610427856445
Thread io: 5.60230565071106
process cpu: 4.07219386100769
Process io: 3.383375406265259
结论:进程的效率明显提高,由于GIL影响线程效率较低
进程和线程的区别和联系
1.两者都是多任务编程的方式,都能够使用计算机多核资源
2.进程的创建和删除要比线程消耗更多计算机资源
3.进程空间独立,数据安全性好,有专门的进程间通信方法
4.线程使用全局变量通信,更加简单,但是往往要与同步互斥机制操作
5.一个进程可以包含多个线程,线程共享进程的资源
6.进线程都有自己的特有属性资源,如命令,属性,id等
使用场景 :
* 需要创建较多的并发,任务比较简单时,线程比较合适
* 如果数据操作和功能比较独立,此时使用进程比较合适
* 使用线程时要考虑到同步互斥的复杂程度
* Python线程需要考虑到GIL问题
总结 :
1. 进程线程的特征
2. 进程线程区别和关系
3. 同步互斥的意义,用过什么方法,什么情况下用
4. 进程间通信方式都知道哪些,有什么特点
5. 僵尸进程怎么处理,线程的GIL问题怎么看
6. 给一个情景,问选择进程还是线程,为什么
服务器模型
硬件服务器:主机 集群
厂商:IBM HP 联想 浪潮
软件服务器:编写的服务端程序,依托于硬件服务器运行,提供给用户一定的软件服务
分类:webserver --> 网络后端程序提供网站请求的后端处理和响应
httpserver --> 处理HTTP请求,回复http响应
邮箱服务器 --> 处理邮件
文件服务器 --> 处理文件传输
功能:网络连接,逻辑处理,数据的交互,数据的传输,协议的实现
模型结构: c/s (客户端服务器模型)
b/s (浏览器服务端模型)
服务器目标:处理速度块,数据更安全,并发量大
硬件:更高的配置,集成分布基础,更好的网络速度,更多主机,更好的网路安全性
软件:程序占有更少的资源,更稳定的运行效果,更流畅的运行速度,采用更安全更合适的技术
基础的服务器模型
循环服务器:单进程程序,循环接受客户端请求,处理请求,每处理完一个请求再去接受下一个请求。
优点:实现简单,占用资源少
缺点:无法同时连接多个客户端,当一个客户端长期占有服务器时,形成其他客户端无法操作的情况
使用情况:任务比较短暂,udp套接字更合适
并发服务器:同时处理多个客户端的任务请求
IO并发:IO多路复用 协程
优点:资源消耗少,效率较高,适用于IO类型服务器
缺点:不能监控cpu密集型程序,本质是单进程,所以不能长期阻塞消息的收发
多进程/多线程并发:为每一个客户端单独提供一个进程或者线程处理请求。由于进程线程执行独立,所以对其他进程不会有影响
优点:客户端可以长期占有服务器,操作不会对其他进程线程产生影响
缺点: 消耗资源较多
多进程并发
使用fork完成并发
1.创建套接字,绑定,监听
2.等待接受客户端请求 accept
3.创建子进程处理客户端请求,父进程继续等待其他客户端连接
4.客户端退出则子进程退出
from socket import *
import os,sys
import signal
#地址
HOST = "0.0.0.0"
PORT = 8888
ADDR = (HOST,PORT)
#处理客户端请求函数
def client_handler(c):
try:
print("子进程接收客户端请求",c.getpeername())
while True:
data = c.recv(1024).decode()
if not data:
break
print(data)
c.send(b'Receive your message')
except (KeyboardInterrupt,SystemExit):
sys.exit(0)
except Exception as e:
print(e)
c.close()
sys.exit(0) #结束子进程
#创建套接字
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ADDR)
s.listen(5)
print("父进程%d等待客户端连接请求"%os.getpid())
# 处理僵尸
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
while True:
try:
c,addr = s.accept()
except KeyboardInterrupt:
sys.exit("服务器退出")
except Exception as e:
print(e)
continue
#创建子进程
pid = os.fork()
if pid < 0 :
print("创建子进程失败")
c.close()
continue
elif pid == 0:
s.close()
#处理客户端请求
client_handler(c)
else:
c.close()
continue