周总结
输入设备>>>CPU>>>输出设备
利用率低,输入设备卡片要全部读完,才能给CPU读代码,但是一个程序员就能操控。
缩短录入数据的时候,CPU连续工作的时间变长,提高CPU利用率。
现代计算机的雏形,大大提高CPU的利用率
操作系统的发展史就是CPU利用率提升的发展史。
切换(服务员针对不同的顾客等待时间去服务其他顾客)
保存状态(服务员记录当下等待顾客的情况,回来再服务每个顾客当时的阶段)
每次切换前还要记录当时执行的状况,后回来就基于当前状态继续执行。
CPU两种状况下会切换:IO操作(输入输出,time.sleep(),读取文件,保存文件),程序长时间占用CPU。
多道比单道更节省时间,提高效率。
程序是写在文件里的代码,而进程就是运行这些代码。
并行:多个进程同时执行。
并发:多个进程看上去同时执行效果,单个CPU也能实现并发,并行也属于并发。
用于描述任务的提交状态
同步:提交完任务之后原地等待任务的结果,期间不做任何事。
异步:提交完任务之后不会原地不动等待结果,伪五杀直接去做其他事情,结果会自动提醒。
用于描述进程执行的状态
阻塞位于阻塞态,非阻塞位于就绪态运行态。
共分为四种状态:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。
同步阻塞:在银行排队,并且在队伍中干等。
同步非阻塞:在银行排队,并且在队伍中做点其他事。
异步阻塞:取号,在旁边座位上等着叫号,期间不做事。
异步非阻塞:取号,在旁边座位上等着叫号,期间不做其他事情。
创建进程的代码在不同的操作系统中,底层原理有区别。
在windows中,创建进程类似于导入模块。
if name == 'main' : 启动脚本
在mac、linux中,创建进程类似于直接拷贝。
不需要启动脚本,但是为了兼容性也能使用。
multiprocess.process
模块补充:
主进程等待子进程运行结束后再运行
如何让子进程在主进程前运行,如果用time.sleep()
是合理的,可以用join
方法。
可以让join()
的位置也会影响程序运行步骤。
多个进程数据彼此间是默认相互隔离的。
Queue模块
1.Queue() 括号内指定队列可容纳的数据个数。 2.put() 向队列中添加数据 3.get() 从队列中取出数据 4.full() 判断队列是否已存满 5.get_nowait() 也是从队列中取出数据,如果队列中没有数据可取,就直接报错。 6.empty() 判断队列是否已空
IPC进制 1.主进程与子进程 2.子进程与子进程通信
1.生产者:产生数据 2.消息队列:支持数据临时保存一下(数据库) 3.消费者:处理数据(筛选)
1.查看进程号 '当前的进程号' current_process().pid '进程号' os.getpid() '父进程的进程号' os.getppid() 2.销毁子进程 p.terminate() 3.判断进程的存活 p.is_alive()
守护:伴随着守护对象的存活而存活,死亡而死亡。
将子进程设置为守护进程:主进程结束,子进程立刻结束。
僵尸进程:进程已经运行结束,但相关资源没有完全清空。最后需要父进程参与回收。
孤儿进程:父进程意外死亡或终止,子进程正常运行,该子进程为孤儿,但是操作系统会做自动将这些孤儿进程存放类似于福利院接收。
互斥锁:是一种用于多线程编程,防止两条线程同时对同一公共资源进行读写机制。
对于模拟多个用户同时对有限个数的票做抢票可以用到这个知识点。
加锁处理:(就是将这个购票行为由并发变成了串行,牺牲了运行效率,但保证数据的安全。)
定义:避免数据错乱,多个程序同时操作一份数据的时候很容易产生数据错乱,为了避免数据错乱,我们可以使用互斥锁。
作用:将并发编程串行,虽然牺牲了程序的执行效率但保证了数据的安全。
from multiprocessing import Process,Lock mutex = Lock() mutex.acquire() 抢锁 mutex.release() 释放锁
互斥锁只要求在多个程序操作的地方,其他位置尽量不要加。
进程其实是资源单位
线程是执行单位
一个进程至少含有一个线程
多进程与多线程的区别
同一进程下多个线程之间资源共享
一般方式
from threading import Thread from multiprocessing import Process import time def task(name): print(f'{name}正在运行') time.sleep(3) print(f'{name}运行结束') t = Thread(target=task, args=('xz', )) #向炒作系统提示要创建一个线程 t.start() print('主线程')
继承线程类的实例化对象
class MyThread(Thread): def __init__(self, name): super().__init__() self.name = name def run(self): print(f'{self.name}正在运行') time.sleep(3) print(f'{self.name}运行结束') obj = MyThread('xz') obj.start() print('主线程')
比多进程更加简单方便,消耗的资源更少。
主线程结束不会使整个线程结束,需要等着其他线程结束才真正结束。
线程间共用同一进程下的资源,数据间可相互影响。
1.进程号 同一进程开设的多个线程拥有相同进程号 2.线程名(current_thread().name) from threading import Thread, current_thread current_thread().name 主:MainThread 子:Thread-N 3.进程下的线程数 active_count()
from threading import Thread import time def task(): print('子线程运行task函数') time.sleep(2) print('子线程运行task结束') t = Thread(target=task) t.daemon = True t.start() t.daemon = True print('主线程') 1.开启守护线程的话,子线程伴随着主线程结束而结束。 2.不开启守护线程的话,主线程要等全部子线程结束而结束。 3.在子线程中代码是异步执行操作,先执行子线程,然后再异步到主线程,最后以子线程结束而整个运行结束。 4.但是守护线程不等最后的子线程结束而结束,而是第二阶段主线程结束而结束。
python解释器由编程语言写出来的。
Cpython 用C写出来的 Jpython 用Java写出来的 Pypython 用python写出来的 最常用的就是Cpython(默认)
重要理解: 1.GIL 的研究是Cpython解释器的特点,不是python语言的特点。 2.GIL 本质是一把互斥锁 3.GIL 的存在使得同一进程下的多个线程无法同时执行。 就是说单进程下的多个进程无法利用多核优势,效率低。 4.GIL 的存在主要是因为Cpython解释器中垃圾回收机制不是线程安全的。
1.中间的IO操作,被迫将GIL释放,造成多个线程同时进行效果。 2.如果中间没有IO操作,GIL会使多线程没办法利用多核优势,架构并发变成串行。 3.所以如果想要有IO操作有要并发,我们需要加锁,加其他的锁。
单核 | 多核 | |
---|---|---|
IO密集型(代码有IO操作) | 1.多进程:申请额外的空间,消耗更多的资源。 2.多线程:消耗资源相对比较小,通过多道技术 3.多线程 优于 多进程 |
1.多进程:申总耗时(单个进程的耗时+10+申请空间+拷贝代码) 2.多线程:总耗时(单个线程的耗时) 3.多线程 略优于 多进程(耗时差距不算大) |
计算机密集型(代码没有IO操作) | 1.多进程:申请额外的空间,消耗更多的资源(总耗时+申请空间+拷贝代码+切换) 2.多线程:消耗资源相对较小,通过多道技术(总耗时+切换) 3.多线程 优于 多进程 |
1.多进程:总耗时(单个进程的耗时) 2.多线程:总耗时(多个进程的综合) 3.多进程 优于 多线程 |
在实际项目中我们应该尽量少用互斥锁,因为可能会发生死锁现象。
所谓死锁就是两个或两个以上的线程需要当前进行的步骤需要该锁的时候,拿到的却是彼此锁。
本质也是互斥锁,它可以产生多把锁。
并发编程中,信号不是我们所指的信号,而是多把互斥锁。
from threading import Thread, Lock, Semaphore import time import random sp = Semaphore(5) # 一次性产生五把锁 class MyThread(Thread): def run(self): sp.acquire() print(self.name) time.sleep(random.randint(1, 3)) sp.release() for i in range(10): t = MyThread() t.start()
子进程/子线程之间可以等待彼此:子A运行到某个代码位置发送信号给子B开始运行。
如果是无限制开进程和线程在实际应用中是实现不了的,因为会造成内存溢出和还会受限制于硬件承受范围。
池: 降低程序的执行效率,保证计算机硬件的安全 进程池: 提前创建好固定个数的进程提供程序去使用,后续不会创建。 线程池: 提前准备好固定个数的线程提供程序去使用,后续不会再创建。
1.pool = ProcessPoolExecutor(5) #固定产生五个进程 2.pool =ThreadPoolExecutor(5) #固定产生五个线程 3.异步回调:异步任务完成后有结果就会触发该机制 4. pool.submit(task, 123).add_done_callback(func)
单线程下实现并发(效率极高),在代码层面上欺骗CPU,让CPU觉得我们是没有IO操作的程序。
'''固定编写 用于检测所有的IO操作(猴子补丁)''' from gevent import monkey; monkey.patch_all() from gevent import spawn
实际上IO操作被我们自己编写的代码检测,一旦有,就会立刻切走,执行没有IO操作,有些变向的等待IO结束。(自己写代码,然后自己切走保存)
import socket from gevent import monkey;monkey.patch_all() # 固定编写 用于检测所有的IO操作(猴子补丁) from gevent import spawn def communication(sock): while True: data = sock.recv(1024) print('服务端接收客户端消息>>>: %s' % data.decode('utf8')) sock.send(data.upper()) def get_server(): server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) while True: sock, address = server.accept() # IO操作 spawn(communication, sock) #sock 支持传参 s1 = spawn(get_server) s1.join()
文本文件
文件路径和数据格式不一样。
软件开发目录规范
规定了数据文件的大致存储位置,但是格式没有完全统一。
数据库服务
统一存取位置,也统一数据格式。
由单击到网络游戏。
单机是不同计算机上相同程序数据无法共享,数据库服务全部在本地完成。
网络游戏是不同计算机上相同程序,数据可以共享。
需联网到数据库服务端,数据库服务端单独在网络架设。
数据库集群:1.数据安全性 2.服务负载
可以让多台服务器运行相同的数据库服务。
数据库三字不同角度下描述不同:
不做特殊说明下数据库其实是数据库软件。其本质也是cs架构的应用程序。
关系型数据库
MySQL、PostgreSQL、MariaDB、ORACLE、sqlite、db2、sql server
非关系型数据库
redis、mongoDB、memcache
show databases; 查看所有的数据库 show tables; 查看所有的表 select * from mysql.user; 查看user表里面所有的记录 注意: SQL语句结束符是分号 ; 取消SQL语句的执行 \c
1.增 create database 库名; 2.查 show databases; #列出所有文件夹--库 show create database 库名; #查指定库 3.改 alter database 库名 charset='gbk'; #改编码格式 4.删 drop database 库名; 查看当前所在的库名 select database(); 如果没有这个库的话就默认为null use 库名 切换到存在的库里,操控该库的数据
1.增 create table 表名(字段名 字段类型,字段名 字段类型,字段名 字段类型); 2.查 show tables; show create table 表名; describe 表名; desc 表名; 3.改 alter table 旧表名 rename 新表名; # 改表名 4.删 drop table 表名;
1.增 insert into 表名 values(数据,数据); insert into 表名 values(数据,数据),(数据,数据),(数据,数据); 2.查 select * from 表名; # *表示查看所有字段 select 字段1,字段2 from 表名; ps:如果表中字段较多出现了错乱 可以结尾写\G 3.改 update 表名 set 字段名=新数据 where 筛选条件; 4.删 delete from 表名; # 删除表中所有的数据 delete from 表名 where 筛选条件 # 按照条件删除数据
版本问题:尽量使用稳定的版本做数据交互的时候。新版本容易不稳定。
但是用在本地也是好用的。
启动服务器
查找mysqld.exe文件位置
打开后cmd转到mysqld.exe文件的路径下,敲入mysqld,这是数据库服务端不要关闭。
再次开启新的cmd窗口
敲入mysql会以游客模式进入,功能很少。
用户名密码登录
mysql -u用户名 -p密码
mysql默认管理员账户,用户名是root 密码是空。
退出:exit、quit
可以将文件路径添加环境变量,这样我们可以在任意位置都能找到。
将mysql服务端制作成系统
以管理员身份
打开cmd窗口
执行系统服务命令
mysqld -- install
启动服务端(两种方式)
此电脑(计算机)
管理,点击服务
,往下拉找到对应的mysql
直接点击启动
net start mysql
1. 查看系统服务的命令----services.msc 2. 关闭mysql服务端---- net stop mysql 3. 移除系统服务 先确保服务已经关闭 执行移除命令mysqld -- remove
修改密码
mysqladmin命令 1.mysqladmin -u用户名 -p原密码 password 新密码 2.偏门方式(有些版本无法使用):#需要登录后才能修改 set password = PASSWORD(新密码)
忘记密码
方法一:直接重装|拷贝对应文件 方法二:先关闭服务端,然后以不需要检验用户身份的方式开启,再修改密码,后再安装正常方式启动 1.net stop mysql 2.mysql --skip-grant-tables 3.mysql -uroot -p 4.update mysql.user set password=password(123) where Host = 'localhost' and User = 'root'; 5.net sart mysql
数据库的服务支持各种语言充当客户端, 为了能够兼容所有类型的客户端。