目录
一、操作系统
操作系统(英语:operating system,缩写作 OS)是管理计算机硬件与软件资源的计算机程序,同时也是计算机系统的内核与基石。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。
操作系统:Dos Windows ios android linux unix
二、多任务
同一时间多个任务同时进行,例如明星开演唱会的时候边唱歌边跳舞。
对于电脑来说多任务就是同时运行多个程序,例如qq,微信,浏览器等同时在电脑上运行。
1.电脑实现多任务的原理
单核cpu
一个头一个大脑运算
同一时间只有一个程序能够运行
之所以看起来电脑同时能执行多个任务,是因为cpu高速切换的原理,肉眼根本不可见,其实实际上同一时间戳只有一个程序能够运行
双核/多核cpu
一个头多个大脑运算
单核 双核
2.查看cpu
计算机---->右键属性---->设备管理器---->处理器
三、进程
1.什么是进程
当程序运行起来的时候就是一个进程,当程序不运行的时候,就是一个程序。
例如:登上QQ运行,QQ就是进程,当退出QQ,QQ就是一个程序。
一个程序对应多个进程
进程是系统进行资源分配和调度的基本单位
ctrl+shift+esc打开windows任务管理器
2.创建进程
import time
import multiprocessing
def sing():
for i in range(3):
print('正在唱歌...',i)
time.sleep(1)
def dance():
for i in range(3):
print('正在跳舞。。。',i)
time.sleep(1)
def main():
#创建两个进程
#执行target目标
p1=multiprocessing.Process(target=sing)
p2=multiprocessing.Process(target=dance)
p1.start()
p2.start()
if __name__=='__main__':
main()
print('程序结束')
当所有子进程执行完后,主进程才会结束
3.进程的状态
在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞。
(1)就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
(2)执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
(3)阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
4.进程之间的通信
(1)引入
(2)队列queue
(3)传递参数
(4)通信
import time
from multiprocessing import Process,Queue
def download_data(q):
lst=['a','b','c']
for i in lst:
q.put(i)#将下载的数据保存到队列中
print('下载完了数据')
def process_data(q):
for i in range(q.qsize()):
print(q.get(i))
print('处理完了数据')
def main():
#先创建一个队列
q=Queue()
#创建两个子进程
q1=Process(target=download_data,args=(q,))
q2=Process(target=process_data,args=(q,))
q1.start()#谁先抢到cpu谁先执行
q2.start()
if __name__=='__main__':
main()
子进程谁先抢到cpu谁就先执行
利用time.sleep()固定谁先执行
5.进程池
当创建的子进程不多的时候,可以直接用multiprocessing模块创建子进程,但如果子进程太多了,用multiprocessing模块创建就会浪费资源。我们可以创建指定个数子进程,例如只创建10个子进程,让这10个子进程重复进行,节约资源。
四、线程
1.线程概念
由于进程是资源拥有者,由于创建、撤销与切换存在较大的内存开销,因此需要引入轻型进程即线程,进程是资源分配的最小单位,线程是cpu调度的最小单位,程序真正执行的时候调用的是线程,每个进程至少拥有一个线程。
2.进程和线程之间的关系
3.使用threading模块
(1)线程引入
(2)传递参数
import time
import threading
def sing(num):
for i in range(num):
print('正在唱歌...',i)
time.sleep(1)
def dance(num):
for i in range(num):
print('正在跳舞。。。',i)
time.sleep(1)
def main():
t1=threading.Thread(target=sing,args=(3,))#创建t1子线程
t2=threading.Thread(target=dance,args=(3,))
t1.start() #开启子线程
t2.start()
if __name__=='__main__':
main()
print('程序结束了')
(3)join方法
join()方法功能:当前线程执行完后其他线程才会继续执行。
(4)setDaemon方法
setDaemon()将当前线程设置成守护线程来守护主线程:
-当主线程结束后,守护线程也就结束,不管是否执行完成。
-应用场景:qq 多个聊天窗口,就是守护线程。
注意:需要在子线程开启的时候设置成守护线程,否则无效。
第一种
第二种
(5)实例方法
线程对象的一些实例方法,了解即可
- setName(): 设置线程的名称。
- getName(): 获取线程的名称。
- is_alive()或isAlive(): 判断当前线程存活状态。
(6)threading模块中的方法
threading.current_thread()返回当前的线程变量
threading.enumerate()返回一个目前正在运行的线程变量的列表
threading.active_count()返回还未结束的线程的个数
4.使用继承方式开启线程
import threading,time
#1.继承threading.Thread类
class MyThread(threading.Thread):
def __init__(self,num):
super().__init__()#调用父类的__init__方法
self.num=num
#2.复写父类的run方法
def run(self):
for i in range(self.num):
print('i--->',i)
time.sleep(1)
if __name__=='__main__':
my_thread=MyThread(3)
my_thread.start()
5.多线程共享变量
6.共享全局变量的问题
多线程开发的时候共享全局变量会带来资源竞争效果。也就是数据不安全。
7.同步异步
(1)同步
同步的意思就是协同步调,按预定的先后次序执行。例如你先说完然后我再说。
大家不要将同步理解成一起动作,同步是指协同、协助、互相配合。
例如线程同步,可以理解为线程A和B一块配合工作,A执行到一定程度时要依靠B的某个结果,于是停下来示意B执行,B执行完将结果给A,然后A继续执行。
A强依赖B(对方),A必须等到B的回复,才能做出下一步响应。即A的操作(行程)是顺序执行的,中间少了哪一步都不可以,或者说中间哪一步出错都不可以。
举个例子:
你去外地上学(人生地不熟),突然生活费不够了;此时你决定打电话回家,通知家里转生活费过来,可是当你拨出电话时,对方一直处于待接听状态(即:打不通,联系不上),为了拿到生活费,你就不停的oncall、等待,最终可能不能及时要到生活费,导致你今天要做的事都没有完成,而白白花掉了时间。
(2)异步
异步则相反,A并不强依赖B,A对B响应的时间也不敏感,无论B返回还是不返回,A都能继续运行;B响应并返回了,A就继续做之前的事情,B没有响应,A就做其他的事情。也就是说A不存在等待对方的概念。
举个例子:
在你打完电话发现没人接听时,猜想:对方可能在忙,暂时无法接听电话,所以你发了一条短信(或者语音留言,亦或是其他的方式)通知对方后便忙其他要紧的事了;这时你就不需要持续不断的拨打电话,还可以做其他事情;待一定时间后,对方看到你的留言便回复响应你,当然对方可能转钱也可能不转钱。但是整个一天下来,你还做了很多事情。 或者说你找室友临时借了一笔钱,又开始happy的上学时光了。
对于多线程共享全局变量计算错误的问题,我们可以使用线程同步来进行解决。
8.互斥锁
当多个线程几乎同时修改一个共享数据的时候,需要进行同步控制,线程同步能够保证多个线程安全的访问竞争资源(全局内容),最简单的同步机制就是使用互斥锁。
某个线程要更改共享数据时,先将其锁定,此时资源的状态为锁定状态,其他线程就能更改,直到该线程将资源状态改为非锁定状态,也就是释放资源,其他的线程才能再次锁定资源。互斥锁保证了每一次只有一个线程进行写入操作。从而保证了多线程下数据的安全性。
9.死锁
在多个线程共享资源的时候,如果两个线程分别占有一部分资源,并且同时等待对方的资源,就会造成死锁现象。
如果锁之间相互嵌套,就有可能出现死锁。因此尽量不要出现锁之间的嵌套。
10.线程队列Queue
队列是一种先进先出(FIFO)的存储数据结构,就比如排队上厕所一个道理。
1.创建一个“队列”对象
import Queue # 导入模块
q = Queue.Queue(maxsize = 10)
Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
2.将一个值放入队列中 q.put(10)
调用队列对象的put()方法在队尾插入一个项目。
3.将一个值从队列中取出q.get()
从队头删除并返回一个项目。如果取不到数据则一直等待。
4.q.qsize() 返回队列的大小
5.q.empty() 如果队列为空,返回True,反之False
6.q.full() 如果队列满了,返回True,反之False
7.q.put_nowait(item) ,如果取不到不等待,之间抛出异常。
8.q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
9.q.join() 收到q.task_done()信号后再往下执行,否则一直等待。或者最开始时没有放数据join()不会阻塞。
q.task_done() 和 q.join() 通常一起使用。
11.生产者与消费者模型
例如A是爬取的数据,B是处理数据的,如果A爬取的快,B处理的慢,那么A就必须等B处理完才能继续爬取数据;
如果B处理数据能力大于A,那么B就必须等待A。为了解决这个问题就引入了生产者和消费者模式。
(1)什么是生产者消费者模式
生产者和消费者之间不直接通信,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
import queue,threading
q=queue.Queue()
lock=threading.Lock()
def producer(name):
count=1
while count<=100:
q.join()#等待task_done()发送的信号
lock.acquire()
q.put(count)
print('{}正在做第{}碗面条'.format(name,count))
count+=1
lock.release()
def customer(name):
count=1
while count<=100:
data=q.get()
lock.acquire()
print('{}正在吃第{}碗面条'.format(name,data))
count+=1
lock.release()
q.task_done()#吃完后发送信号
def main():
t1=threading.Thread(target=producer,args=('赵四',))
t2=threading.Thread(target=customer,args=('小宝',))
t1.start()
t2.start()
if __name__=='__main__':
main()
12.GIL全局解释锁
GIL 即 :global interpreter lock 全局解释锁。
在进行GIL讲解之前,我们可以先了解一下并行和并发:
并行
多个CPU同时执行多个任务,就好像有两个程序,这两个程序是真的在两个不同的CPU内同时被执行。
并发
CPU交替处理多个任务,还是有两个程序,但是只有一个CPU,会交替处理这两个程序,而不是同时执行,只不过因为CPU执行的速度过快,而会使得人们感到是在“同时”执行,执行的先后取决于各个程序对于时间片资源的争夺。
并行和并发同属于多任务,目的是要提高CPU的使用效率。这里需要注意的是,一个CPU永远不可能实现并行,即一个CPU不能同时运行多个程序。
Guido van Rossum(吉多·范罗苏姆)创建python时就只考虑到单核cpu,解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁, 于是有了GIL这把超级大锁。因为cpython解析只允许拥有GIL全局解析器锁才能运行程序,这样就保证了同一个时刻只允许一个线程可以使用cpu。也就是说多线程并不是真正意义上的同时执行。