网络编程03--进程部分+线程
昨日总结
- 操作系统的发展史
- 穿孔卡片
cpu利用率极低 - 联机批处理系统
cpu利用率有所提升 - 脱机批处理系统
cpu效率极大提升(现代计算机的雏形) - 多道技术(单核cpu)
- 串行:多个任务依次排队执行
- 多道:切换+保存状态
- 穿孔卡片
- 进程理论
- 程序与进程的区别
程序是死的,进程是活的 - 进程的调度算法
- 先来先服务
- 短作业优先
- 时间片轮转发+多级反馈队列
- 进程的三状态图
- 就绪态
- 运行态(只有经过就绪态的进程才可以进入运行态)
- 阻塞态
- 任务的提交方式
- 同步:提交任务之后原地等待任务结果,期间不做任何事情
- 异步:提交任务之后不愿地等待,结果由反馈机制(异步回调机制)提交
- 进程的状态
- 阻塞态:阻塞态
- 非阻塞态:就绪态,运行态
- 高效的程序尽量一直处在非阻塞态
- 异步非阻塞:效率最高
- 程序与进程的区别
- 开启进程的多种方式
- 函数启动
- 类启动
- 注:平时多留意高端的内置模块
.start()启动,异步提交,告诉操作系统开设进程
- 进程join方法
- 主进程等待子进程运行结束之后再执行
- 注意多个进程在等待其中一个执行的时候另外几个也相当于在执行
- 进程对象方法
- 获取进程好
- 获取进程名称
- 杀死进程
- 判断进程是否存活
- 进程间数据是默认隔离的
# 名称空间
global
nonlocal
1、僵尸进程与孤儿进程
1.1、僵尸进程
进程代码运行结束之后并没有直接结束而是等待回收子进程资源才结束
from multipocessing import Process
import time
def test(name):
print('%s is running'% name)
time.sleep(3)
print('%s is over'% name)
if __name__ == '__main__':
p = Process(target=test,args=('kk',))
p.start()
print('主')
1.2、孤儿进程
即主程序死亡(非正常死亡)但是子程序还在运行
孤儿进程对操作系统是有害的
2、守护进程
守护进程:守护着某个程序,一旦这个进程结束那么也随即结束(放置孤儿进程)
from multiprocessing import Process
import time
def test(name):
print('%s is running'% name)
time.sleep(3)
print('%s is over' % name)
if __name__ == '__main__':
p = Process(target=test,args=('kk',))
p.start()
print('主')
2、互斥锁(重要)
问题:并发情况下操作同一份数据,极其容易造成数据混乱
解决:将并发变成串行,虽然降低了效率但是提升了数据安全
锁就可以实现将并发变成串行的效果
行锁,表锁:解决多人操作的情况下数据不混乱
使用锁的注意事项:一定要在主进程中产生,交由子进程使用
- 一定要在需要的地方加锁,入更改数据,千万不要随意加锁
- 不要轻易的使用锁(死锁现象)
from multiprocessing import Process
import json
import time
import random
# 查票
def search(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
data_dict = json.load(f)
tivket_nu = data_dict.get('ticker_num')
print('%s查询票数:%s' %(name, ticker_num))
# 买票
def buy(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
data_dict = json.load(f)
tivket_nu = data_dict.get('ticker_num')
# 模拟延迟
if ticket_num > 0:
data_dict['dicket_num'] -= 1
with open (r'data.txt', 'w', encoding='utf8') as f:
json.dump(data_dict)
print('%s买票成功'%name)
else:
print('没票了')
def run(name):
search(name)
buy(name)
if __name__ == '__main__':
for i in range(10):
p = Process(target=run, args=('用户%s'% i,))
p.start()
"""上述存在数据错乱问题"""
from multipeocessing import Peocess, Lock
import json
import time
import random
# 查票
def search(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
data_dict = json.load(f)
tivket_num = data_dict.get('ticker_num')
print('%s查询余票:%s'%(name, tivket_num))
# 买票
def buy(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
data_dict = json.load(f)
tivket_num = data_dict.get('ticker_num')
if tivket_num > 0:
data_dict['ticker_num'] -= 1
with open(r'data.txt', 'w', encoding='utf8') as f:
json.dump(data_dict)
print('%s买票成功'%name)
else:
print('没票了')
def run(name, mutex):
search(name)
mutex.acquire() # 抢锁
buy(name)
mutex.release() # 上一个进程处理完成后会把锁释放,其他进程继续抢锁
if __name__ == '__main__':
mutex = Lock() # 产生锁对象
for i in range(1,11): # 循环
p = Process(target=run, args=('kk',)) # 长生进程对象
p.start() # 启动进程对象
3、消息队列
队列:先进先出
from multipeocessing import Queue # 调用队列
q = Queue(5) # 队列大小
'''存数据'''
q.put(111) # 存放数据
q.put(222) # 存放数据
q.put(333) # 存放数据
print(q.full) # 判断队列是否为满,返回布尔值
print(q.empty()) # 判断队列是否为空,返回布尔值
q.put(444) # 存放数据
q.put(555) # 存放数据
q.put(666) # 这个存放不进到队列中,因为队列大小为5,超出部分原地等待队列中数据被其他进程或许线程取走
print('学习啊') # 这是后是没有输出的
'''取数据'''
print(q.get()) # 取数据
print(q.get()) # 取数据
print(q.get()) # 取数据
print(q.get()) # 取数据
print(q.get()) # 取数据
print(q.get()) # 到这里程序会原地阻塞,等待其他线程或者进程存入数据
print(q.get_nowait()) # 没有数据立即报错
print('不学了') # 这个同样没办法输出
q.get() # 取数据
q.put() # 存数据
q.get_nowait() # 取数据,没有数据直接报错
q.full() # 判断队列是否为满,返回布尔值
q.empty() # 判断队列是否为空
'''
full()和get_nowait()能否用于多进程的情况下精准使用
一定不能!!!
存在极限条件
1.当队列满了之后full()判断为True,可是一瞬间被其他进程取走数据那么判断就不准确了
2.get_nowait()同上
列队的使用就可以打破进程间默认无法通信的情况
'''
4、IPC机制
'''IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程'''
'''子进程与子进程数据交互'''
from multiprocessing import Queue, Process
def producer(q):
q.put('子进程p放的数据')
def consumer(c):
print('子进程c取的数据', c.get())
if __name__ == '__main__':
q = Queue() # 产生队列对象
p = Process(target = producer, args=(q,)) # 产生子进程
c = Process(target = consumer, args=(q,)) # 产生子进程
p.start()
c.start()
'''主进程与子进程数据交互'''
from multiprocessing import Queue, Process
def consumer(q):
ptint('取出主进程存放的数据', q.get())
print('子进程存放的数据')
if __name__ == '__main__':
q = Queue()
q.put('主进程')
p = Process(target=consumer, args=(q,))
p.start() # 启动子进程
p.join() # 等待子进程运行完毕,在运行主进程
print('主')
5、生产者与消费者模型
'''
生产者:
负责生产数据
消费者:
负责处理数据
该模型需要解决供需不平衡现象
爬虫领域使用较多
'''
from multiprocessing import Queue, Process, JoinableQueue
import time
import random
'''
JoinableQueue:
可以记住消息队列中存取数据的数量
'''
def producer(name, food, q):
for i in range(10):
print('%s生产了%s'% (name, food))
q.put(food)
time.sleep(random.random())
def consumer(name, q):
while True:
data = q.get()
print('%s吃了%s'%(name, data))
q.task_done() # 检测队列中还有多少数据
if __name__ == '__main__':
q = JoinableQueue()
p1 = Process(target=producer, args=('大厨', '玛莎拉', q))
p2 = Process(target=producer, args=('印度阿三', '飞饼', q))
p3 = Process(target=producer, args=('泰国阿人', '榴莲', q))
c1 = Process(target=consumer, args=('阿飞', q))
p1.start()
p2.start()
p3.start()
c1.daemon = True # 守护进程模式:等待主进程结束之后才会结束
c1.start()
p1.join()
p2.join()
p3.join()
q.join() # 等待队列中所有的数据被取干净
print('主')
6、线程理论
什么是线程:
进程中CUP真正执行的是现象线程,进程只是一个资源单位
进程类似于是工厂,线程类似于是工厂里面的流水线
所有的进程都必须有一条线程
# 进程之间的数据默认是隔离的,但是同一个进程多个线程数据是共享的
7、线程的开启方式
'''
开设进程需要执行什么
1.重新申请一块内存空间
2.将所有的数据全部导入
开设线程需要执行什么
上述两个步骤都不需要,消耗的资源少
'''
# 函数开启线程
from threading import Thread
import time
def test(naem):
print('%s运行中'% name)
time.sleep(3)
print('%s结束'%name)
# 开设线程不需要__main__
t = Thread(target=test, args=('kk',))
t.start()
print('主')
# 类开启
class MyClass(Thread): # 继承类
def __init__(self, name): # 重写__init__,对象传入参数
super().__init__() # 假设不知道原来的__init__方法内部有什么,所有再次调用原__init__()方法
self.name = name
def run(self):
print('%s运行中'% name)
time.sleep(3) # 原地等待、三秒
print('%s结束中'% name)
obj = MyClass('kk') # 调用类,传入参数
obj.start()
print('主')
8、线程对象的其他方法
'''
1、join方法
等待子线程运行之后运行主线程
'''
from threading import Thread
import time
def test(name):
print('%s运行中'%name)
time.sleep(3)
print('%s结束中'%name)
t = Thread(target=test, args=('kk',))
t.start()
t.join
print('主')
'''
2、os模块,获取当前线程的进程号
'''
from threading import Thread
import time
def test(name):
print(os.getpid())
print('%s运行中'%name)
time.sleep(3)
print('%s结束中'%name)
t = Thread(target=test, args=('kk',))
t.start()
t.join()
print(os.getpid())
print('主')
'''
3、active_count获取当前线程活跃数
'''
from threading import Thread, active_count
import time
def test(name):
print(os.getpid())
print('%s运行中'%name)
time.sleep(3)
print('%s结束中'%name)
t = Thread(target=test, args=('kk',))
t.start()
t.join()
print(active_count()) # 可以直接打印
print('主')
'''
4、aurrent_thread获取当前线程名字
'''
from threading import Thread, active_count, current_thread
import time
# 导入current_thread
def test(name):
print('%s运行中'%name)
time.sleep(3)
print('%s结束中'%name)
t = Thread(target=test, args=('kk',))
t.start()
t.join()
print(current_thread()) # 可以直接打印
print('主')
9、守护线程
'''
主程序的结束意味着整个进程的结束,守护线程同样结束
主线程需要等待里面所有的非守护线程的结束才结束
'''
from threading import Thread
from multiprocessing import Process
import time
def foo():
print(123)
time.sleep(3)
print("end123")
def bar():
print(456)
time.sleep(1)
print("end456")
if __name__ == '__main__':
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------")
# 问:打印顺序
# 123
# 456
# main-------
# end456
# 答:程序开始运行t1.start()打印123,随即暂停三秒钟,t2运行,打印456,随即暂停1秒钟,主线程运行,打印main,主程序运行完毕,等待非守护进程线程,t2等待时间到达,打印end456,主线程随即结束,t1守护线程同时结束
from threading import Thread
from multiprocessing import Process
import time
def foo():
print(123)
time.sleep(1)
print("end123")
def bar():
print(456)
time.sleep(3)
print("end456")
if __name__ == '__main__':
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------")
# 打印顺序
# 123
# 456
# main-------
# end123
# end456
# t1守护线程运行,中途等待1秒钟,t2运行,中途等待3秒钟,主程序运行,等待非守护进程t2运行完毕,此时守护进程打印,运行完毕,t2运行完毕,整个进程结束
# 原理t1为守护线程模式,t2非守护线程,主程序等到t2运行完毕即结束t1同时结束。
10、线程数据共享
# 同一进程下所有线程数据共享
from threading import Thread
money = 100
def test():
global money
money = 999
t = Thread(target=test)
t.start()
t.join()
print(money)
11、线程互斥锁
# 线程修改数据时不加锁同样会造成数据混乱
from threading import Thread, Lock
from multiprocessing import Lock
import time
num = 100
def test(mutex):
global num
mutex.acquire()
# 先获取num的数值
tmp = num
# 模拟延迟效果
time.sleep(0.1)
# 修改数值
tmp -= 1
num = tmp
mutex.release()
t_list = []
mutex = Lock()
for i in range(100):
t = Thread(target=test, args=(mutex,))
t.start()
t_list.append(t)
# 确保所有的子线程全部结束
for t in t_list:
t.join()
print(num)
12、TCP服务端并发
# 服务端
import socket
from threading import Thread
from multiprocessing import Process
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
def talk(sock):
while True:
try:
data = sock.recv(1024)
if len(data) == 0: break
print(data.decode('utf8'))
sock.send(data + b'gun dan!')
except ConnectionResetError as e:
print(e)
break
sock.close()
while True:
sock, addr = server.accept()
print(addr)
# 开设多进程或者多线程
t = Thread(target=talk, args=(sock,))
t.start()
13、扩展
- 什么是乐观锁与悲观锁
- 乐观锁:
- 原因:假设在更改数据时没有其他的用户更改数据,不会导致数据错乱,所以不加锁
- 应用场景:阅读软件,查询系统。在读取数据,不更改数据的应用上使用
- 悲观锁:
- 原因:假设在更改数据时有其他用户也在更改数据,需要加锁处理否则就会造成数据错乱
- 应用场景:买票软件。更改数据多的情况下
- 乐观锁:
- 消息队列(三种经典场景)
- 异步
- 增加功能,使程序处在异步状态,减少用户等待时间
- 解耦
- 解决代码冗余,更改功能需要更改全部的问题
- 削峰
- 一定时间的内的访问量非常大,防止数据混乱,建立队列,
- 异步
- 23种设计模式
理解为三大类:- 创建型模式:
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 建造者模式
= 原型模式
- 结构型模式:
- 适配器模式
- 装饰器模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
- 行为型模式:
- 策略模式
- 模板方法模式
- 观察者模式
- 迭代子模式
- 责任链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
- 创建型模式:
浙公网安备 33010602011771号