您好!此笔记的文本和代码以网盘形式分享于文末!
因个人能力有限,错误处欢迎大家交流和指正!基础部分内容简单,但多且零散!
"""
# 操作系统
批处理系统:在它的控制下,计算机能够自动地、成批地处理一个或多个用户的作业
(这作业包括程序、数据和命令)
多道程序系统:允许多个程序同时进入内存并运行,多道程序系统的出现,
标志着操作系统渐趋成熟的阶段,先后出现了作业调度管理、处理机管理、存储器管理、
外部设备管理、文件系统管理等功能。同时也开始有了空间隔离和时空复用的概念特点,
分时系统:多用户分时系统是当今计算机操作系统中最普遍使用的一类操作系统,
分时——现在流行的PC,服务器都是采用这种运行模式
实时系统:实时控制系统、实时信息处理系统。
及时响应。每一个信息接收、分析处理和发送的过程必须在严格的时间限制内完成。
高可靠性。需采取冗余措施,双机系统前后台工作,也包括必要的保密措施等。
实时——一般用于单片机上、PLC等
通用操作系统:
通用操作系统:具有多种类型操作特征的操作系统。
可以同时兼有多道批处理、分时、实时处理的功能,或其中两种以上的功能。
现代操作系统:
个人计算机操作系统:个人计算机上的操作系统是联机交互的单用户操作系统
网络操作系统:在原来各自计算机操作系统上,按照网络体系结构的各个协议标准增加
网络管理模块,其中包括:通信、资源共享、系统安全和各种网络应用服务。
分布式操作系统:分布式操作系统也是通过通信网络,将地理上分散的具有自治功能的
数据处理系统或计算机系统互连起来,实现信息交换和资源共享,协作完成任务。
分布式系统更强调分布式计算和处理。
操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序。
一:隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,
更简单,更清晰的模型(系统调用接口)。应用程序员有了这些接口后,
就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
二:将应用程序对硬件资源的竞态请求变得有序化
"""
"""
# 进程
进程:进程即正在执行的一个过程,进程是对正在运行程序的一个抽象。是操作系统最核心的概念
操作系统的其他所有内容都是围绕进程的概念展开的。
#一 操作系统的作用:
1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口
2:管理、调度进程,并且将多个进程对硬件的竞争变得有序
#二 多道技术:
1.产生背景:针对单核,实现并发
ps:
现在的主机一般是多核,那么每个核都会利用多道技术
有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个
cpu中的任意一个,具体由操作系统调度算法决定。
2.空间上的复用:如内存中同时有多道程序
3.时间上的复用:复用一个cpu的时间片
强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样
才能保证下次切换回来时,能基于上次切走的位置继续运行
进程(process)是计算机中的程序关于某数据集合上的一次运行活动,
是系统进行资源分配和调度的基本单位,是操作系统结构的基础.
程序是指令、数据及其组织形式的描述,进程是程序的实体.
狭义上进程是正在运行的程序的实例,
广义上它是操作系统动态执行的基本单元,在传统的操作系统中,
进程既是基本的分配单元,也是基本的执行单元。
进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,
包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
文本区域存储处理器执行的代码;
数据区域存储变量和进程执行期间使用的动态分配的内存;
堆栈区域存储着活动过程调用的指令和本地变量。
进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时
(即被操作系统执行),它才能成为一个活动的实体,我们称其为进程。
从实现角度,进程是一种数据结构,目的在于清晰地刻画动态系统的内在规律,
有效管理和调度进入计算机系统主存储器运行的程序。
进程的特征:
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、
不可预知的速度向前推进。
结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,
能得到不同的结果;但是执行过程中,程序不能发生改变。
同一个程序执行两次,就会在操作系统中出现两个进程,
所以我们可以同时运行一个软件,分别做不同的事情也不会混乱。
进程的调度(进程的调度算法)
1、先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,
也可用于进程调度。FCFS算法比较有利于长作业(进程),而不利于短作业(进程)。
由此可知,本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)。
2、短作业(进程)优先调度算法(SJ/PF)是指对短作业或短进程优先调度的算法,
该算法既可用于作业调度,也可用于进程调度。但其对长作业不利;
不能保证紧迫性作业(进程)被及时处理;作业的长短只是被估算出来的。
3、时间片轮转(Round Robin,RR)法的基本思路是让每个进程在就绪队列中的等待时间
与享受服务的时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片。
4、多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,
而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进程调度算法。
<1>设置多个就绪队列,并为各个队列赋予不同的优先级。
<2>一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。
<3>仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;
进程的并行和并发
并行 : 并行是指两者同时执行
并发 : 并发是指资源有限的情况下,两者交替轮流使用资源
由于操作系统的调度算法
程序会进入的状态:就绪,运行和阻塞
1、就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行
2、执行/运行(Running)状态
当进程已获得处理机,其程序正在处理机上执行。
3、阻塞(Blocked)状态(成因:等待I/O完成、申请缓冲区不能满足、等待信件(信号)等)
正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态
同步:同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,
依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,
两个任务的状态可以保持一致。
异步:异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,
依赖的任务也立即执行,只要自己完成了整个任务就算完成了。
至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,
所以它是不可靠的任务序列。
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。
1、同步阻塞形式
效率最低。拿上面的例子来说,就是你专心排队,什么别的事都不做。
2、异步阻塞形式
如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知),
也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,
这个人被阻塞在了这个等待的操作上面;异步操作是可以被阻塞住的,
只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。
3、同步非阻塞形式
实际上是效率低下的。想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,
如果把打电话和观察排队的位置看成是程序的两个操作的话,
这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的。
4、异步非阻塞形式(*****)
效率更高,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,
程序没有在两种不同的操作中来回切换。比如说,这个人突然发觉自己烟瘾犯了,
需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,
那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。
通用系统的进程创建形式
1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,
前台进程负责与用户交互,后台运行的进程与用户无关,
运行在后台并且只在需要时才唤醒的进程,称为守护进程,
如电子邮件、web页面、新闻、打印)
2. 一个进程在运行过程中开启了子进程(如nginx开启多进程os.fork,subprocess.Popen等)
3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
4. 一个批处理作业的初始化(只在大型机的批处理系统中应用。
进程的结束
1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,
在linux中用exit,在windows中用ExitProcess)
2. 出错退出(自愿,python a.py中a.py不存在)
3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,
try...except...)
4. 被其他进程杀死(非自愿,如kill -9)
"""
"""
multiprocess模块:不是一个模块而是python中一个操作、管理进程的包.
提供了创建进程部分,进程同步部分,进程池部分,进程之间数据共享功能的大量模块。
process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
Process([group [, target [, name [, args [, kwargs]]]]]),
由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
注意:
1. 需要使用关键字的方式来指定参数。
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号。
参数介绍:
1 group参数未使用,值始终为None
2 target表示调用对象,即子进程要执行的任务
3 args表示调用对象的位置参数元组,args=(1,2,'egon',)
4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
5 name为子进程的名称
方法介绍:
1 p.start():启动进程,并调用该子进程中的p.run()
2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,
我们自定义类的类中一定要实现该方法。
3 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,
该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
如果p还保存了一个锁那么也将不会被释放,进而导致死锁
4 p.is_alive():如果p仍然运行,返回True
5 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,
而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,
p.join只能join住start开启的进程,而不能join住run开启的进程
属性介绍:
1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,
当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,
必须在p.start()之前设置
2 p.name:进程的名称
3 p.pid:进程的pid
4 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。
这个键的用途是为涉及网络连接的底层进程间通信提供安全性,
这类连接只有在具有相同的身份验证键时才能成功(了解即可)
# 使用process模块创建进程:
import time
from multiprocessing import Process
def f(name):
print('hello', name)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
time.sleep(1)
print('执行主进程的内容了')
# join 方法
import time
from multiprocessing import Process
def f(name):
print('hello', name)
time.sleep(1)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
#p.join()
print('我是父进程')
# 查看主进程和子进程的进程号
import os
from multiprocessing import Process
def f(x):
print('子进程id :',os.getpid(),'父进程id :',os.getppid())
return x*x
if __name__ == '__main__':
print('主进程id :', os.getpid())
p_lst = []
for i in range(5):
p = Process(target=f, args=(i,))
p.start()
# 多个进程同时运行
import time
from multiprocessing import Process
def f(name):
print('hello', name)
time.sleep(1)
if __name__ == '__main__':
p_lst = []
for i in range(5):
p = Process(target=f, args=('bob',))
p.start()
p_lst.append(p)
# 多个进程同时运行,join方法1
import time
from multiprocessing import Process
def f(name):
print('hello', name)
time.sleep(1)
if __name__ == '__main__':
p_lst = []
for i in range(5):
p = Process(target=f, args=('bob',))
p.start()
p_lst.append(p)
p.join()
# [p.join() for p in p_lst]
print('父进程在执行')
# 多个进程同时运行,join方法2
import time
from multiprocessing import Process
def f(name):
print('hello', name)
time.sleep(1)
if __name__ == '__main__':
p_lst = []
for i in range(5):
p = Process(target=f, args=('bob',))
p.start()
p_lst.append(p)
# [p.join() for p in p_lst]
print('父进程在执行')
# 通过继承Process类开启进程
import os
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(os.getpid())
print('%s 正在和女主播聊天' %self.name)
p1=MyProcess('wupeiqi')
p2=MyProcess('yuanhao')
p3=MyProcess('nezha')
p1.start() #start会自动调用run
p2.start()
# p2.run()
p3.start()
p1.join()
p2.join()
p3.join()
print('主线程')
# 进程之间的数据隔离问题
from multiprocessing import Process
def work():
global n
n=0
print('子进程内: ',n)
if __name__ == '__main__':
n = 100
p=Process(target=work)
p.start()
print('主进程内: ',n)
# 守护进程--会随着主进程的结束而结束。
# 守护进程内无法再开启子进程,否则抛出异常:AssertionError
# 守护进程的启动
import os
import time
from multiprocessing import Process
class Myprocess(Process):
def __init__(self, person):
super().__init__()
self.person = person
def run(self):
print(os.getpid(), self.name)
print('%s正在和女主播聊天' % self.person)
p = Myprocess('哪吒')
# 一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,
# 并且父进程代码执行结束,p即终止运行
p.daemon = True
p.start()
# 在sleep时查看进程id对应的进程ps -ef|grep id
time.sleep(10)
print('主')
# 主进程代码执行结束守护进程立即结束
from multiprocessing import Process
def foo():
print(123)
time.sleep(1)
print("end123")
def bar():
print(456)
time.sleep(3)
print("end456")
p1 = Process(target=foo)
p2 = Process(target=bar)
p1.daemon=True
p1.start()
p2.start()
time.sleep(0.1)
print("main-------")
"""
"""
# socket聊天并发实例
# server端
from socket import *
from multiprocessing import Process
server=socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
server.bind(('127.0.0.1', 8080))
server.listen(5)
def talk(conn,client_addr):
while True:
try:
msg = conn.recv(1024)
if not msg:break
conn.send(msg.upper())
except Exception:
break
# windows下start进程一定要写到这下面
if __name__ == '__main__':
while True:
conn, client_addr = server.accept()
p=Process(target=talk, args=(conn, client_addr))
p.start()
# client端
from socket import *
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))
"""
"""
# 进程对象的其他方法:terminate,is_alive
from multiprocessing import Process
import time
import random
class Myprocess(Process):
def __init__(self, person):
self.name = person
super().__init__()
def run(self):
print('%s正在和网红脸聊天' % self.name)
time.sleep(random.randrange(1, 5))
print('%s还在和网红脸聊天' %self.name)
p1=Myprocess('哪吒')
p1.start()
p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive()) #结果为True
print('开始')
print(p1.is_alive()) #结果为False
# 进程对象的其他属性:pid和name
class Myprocess(Process):
def __init__(self, person):
# name属性是Process中的属性,标示进程的名字
self.name=person
# 执行父类的初始化方法会覆盖name属性
super().__init__()
# 在这里设置就可以修改进程名字了
# self.name = person
# 如果不想覆盖进程名,就修改属性名称就可以了
# self.person = person
def run(self):
print('%s正在和网红脸聊天' %self.name)
# print('%s正在和网红脸聊天' %self.person)
time.sleep(random.randrange(1,5))
print('%s正在和网红脸聊天' %self.name)
# print('%s正在和网红脸聊天' %self.person)
p1 = Myprocess('哪吒')
p1.start()
# 可以查看子进程的进程id
print(p1.pid)
"""
"""
进程同步(multiprocess.Lock) -- 锁 —— multiprocess.Lock
并发编程能更加充分的利用IO资源,多个进程运行无顺序,一旦开启不受控制,
当多个进程使用同一份数据资源时,会引发数据安全或顺序混乱问题。
# 多进程抢占输出资源
import os
import time
import random
from multiprocessing import Process
def work(n):
print('%s: %s is running' %(n,os.getpid()))
time.sleep(random.random())
print('%s:%s is done' %(n,os.getpid()))
if __name__ == '__main__':
for i in range(3):
p=Process(target=work,args=(i,))
p.start()
# 使用锁维护执行顺序
# 由并发变成了串行,牺牲了运行效率,但避免了竞争
import os
import time
import random
from multiprocessing import Process,Lock
def work(lock,n):
lock.acquire()
print('%s: %s is running' % (n, os.getpid()))
time.sleep(random.random())
print('%s: %s is done' % (n, os.getpid()))
lock.release()
if __name__ == '__main__':
lock=Lock()
for i in range(3):
p=Process(target=work,args=(lock,i))
p.start()
# 模拟多进程同时抢票
#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
#并发运行,效率高,但竞争写同一文件,数据写入错乱
from multiprocessing import Process,Lock
import time,json,random
def search():
dic=json.load(open('db'))
print('\033[43m剩余票数%s\033[0m' % dic['count'])
def get():
dic=json.load(open('db'))
# 模拟读数据的网络延迟
time.sleep(0.1)
if dic['count'] > 0:
dic['count'] -= 1
# 模拟写数据的网络延迟
time.sleep(0.2)
json.dump(dic,open('db', 'w'))
print('\033[43m购票成功\033[0m')
def task():
search()
get()
if __name__ == '__main__':
# 模拟并发100个客户端抢票
for i in range(100):
p=Process(target=task)
p.start()
# 使用锁来保证数据安全
#文件db的内容为:{"count":5}
#注意一定要用双引号,不然json无法识别
#并发运行,效率高,但竞争写同一文件,数据写入错乱
from multiprocessing import Process,Lock
import time,json,random
def search():
dic=json.load(open('db'))
print('\033[43m剩余票数%s\033[0m' %dic['count'])
def get():
dic=json.load(open('db'))
time.sleep(random.random()) #模拟读数据的网络延迟
if dic['count'] >0:
dic['count']-=1
time.sleep(random.random()) #模拟写数据的网络延迟
json.dump(dic,open('db','w'))
print('\033[32m购票成功\033[0m')
else:
print('\033[31m购票失败\033[0m')
def task(lock):
search()
lock.acquire()
get()
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(100): #模拟并发100个客户端抢票
p=Process(target=task,args=(lock,))
p.start()
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,
即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全.
加锁的问题:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理
"""
"""
进程间通信IPC(Inter-Process Communication)——队列(multiprocess.Queue)
解决方案:1、效率高;2、帮助处理好锁的问题
mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道
队列和管道都是将数据存放于内存中,
队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,
避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。
Queue([maxsize])
创建共享的进程队列。
参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
底层队列使用管道和锁定实现。还需要运行支持线程以便队列中的数据传输到底层管道中。
方法介绍:
q.get( [ block [ ,timeout ] ] )
返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。
block用于控制阻塞行为,默认为True. 如果设置为False,
将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。
如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。
q.get_nowait( )
同q.get(False)方法。
q.put(item [, block [,timeout ] ] )
将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,
默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。
timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。
q.qsize()
返回队列中目前项目的正确数量。此函数的结果并不可靠,
因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。
在某些系统上,此方法可能引发NotImplementedError异常。
q.empty()
如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,
结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。
q.full()
如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)
其他了解方法:
q.close()
关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列
但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。
关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,
如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
q.cancel_join_thread()
不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。
q.join_thread()
连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。
默认情况下,此方法由不是q的原始创建者的所有进程调用。
调用q.cancel_join_thread()方法可以禁止这种行为。
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
# 队列基本用法
from multiprocessing import Queue
q=Queue(3)
#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
# # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
# # 如果队列中的数据一直不被取走,程序就会永远停在这里。
# q.put(3)
try:
# 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
q.put_nowait(3)
except:
# 因此我们可以用一个try语句来处理这个错误。
# 这样程序不会一直阻塞下去,但是会丢掉这个消息。
print('队列已经满了')
# 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
print(q.full())
print(q.get())
print(q.get())
print(q.get())
# # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
# print(q.get())
try:
# 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
q.get_nowait(3)
except:
# 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
print('队列已经空了')
print(q.empty()) #空了
# 子进程发送数据给父进程
import time
from multiprocessing import Process, Queue
def f(q):
q.put([time.asctime(), 'from Eva', 'hello']) #调用主函数中p进程传递过来的进程参数 put函数为向队列中添加一条数据。
if __name__ == '__main__':
q = Queue() #创建一个Queue对象
p = Process(target=f, args=(q,)) #创建一个进程
p.start()
print(q.get())
p.join()
# 批量生产数据放入队列再批量获取结果 x
import os
import time
import multiprocessing
# 向queue中输入数据的函数
def inputQ(queue):
info = str(os.getpid()) + '(put):' + str(time.asctime())
queue.put(info)
# 向queue中输出数据的函数
def outputQ(queue):
info = queue.get()
print ('%s%s\033[32m%s\033[0m'%(str(os.getpid()), '(get):',info))
# Main
if __name__ == '__main__':
multiprocessing.freeze_support()
record1 = [] # store input processes
record2 = [] # store output processes
queue = multiprocessing.Queue(3)
# 输入进程
for i in range(10):
process = multiprocessing.Process(target=inputQ,args=(queue,))
process.start()
record1.append(process)
# 输出进程
for i in range(10):
process = multiprocessing.Process(target=outputQ,args=(queue,))
process.start()
record2.append(process)
for p in record1:
p.join()
for p in record2:
p.join()
"""
"""
# 生产者消费者模型
生产者就是生产数据的线程,消费者就是消费数据的线程。
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,
所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,
消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,
平衡了生产者和消费者的处理能力。
# 基于队列实现生产者消费者模型
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(q):
for i in range(10):
time.sleep(random.randint(1,3))
res='包子%s' %i
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=(q,))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
#开始
p1.start()
c1.start()
print('主')
# 解决q.get()死循环问题
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1, 3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(), res))
def producer(q):
for i in range(10):
time.sleep(random.randint(1, 3))
res='包子%s' %i
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(), res))
# 发送结束信号
q.put(None)
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer, args=(q,))
#消费者们:即吃货们
c1=Process(target=consumer, args=(q,))
#开始
p1.start()
c1.start()
print('主')
# 主进程在生产者生产完毕后发送结束信号None
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(q):
for i in range(2):
time.sleep(random.randint(1,3))
res='包子%s' %i
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=(q,))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
#开始
p1.start()
c1.start()
p1.join()
q.put(None) #发送结束信号
print('主')
# 多个消费者的例子:有几个消费者就需要发送几次结束信号
from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
while True:
res=q.get()
if res is None:break #收到结束信号则结束
time.sleep(random.randint(1,3))
print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
def producer(name,q):
for i in range(2):
time.sleep(random.randint(1,3))
res='%s%s' %(name,i)
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
if __name__ == '__main__':
q=Queue()
#生产者们:即厨师们
p1=Process(target=producer,args=('包子',q))
p2=Process(target=producer,args=('骨头',q))
p3=Process(target=producer,args=('泔水',q))
#消费者们:即吃货们
c1=Process(target=consumer,args=(q,))
c2=Process(target=consumer,args=(q,))
#开始
p1.start()
p2.start()
p3.start()
c1.start()
p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
p2.join()
p3.join()
q.put(None) #有几个消费者就应该发送几次结束信号None
q.put(None) #发送结束信号
print('主')
# JoinableQueue([maxsize]) :创建可连接的共享进程队列。
像是一个Queue对象,但队列允许项目的使用者通知生产者项目已经被成功处理。
通知进程是使用共享的信号和条件变量来实现的。
# 方法介绍:JoinableQueue的实例p除了与Queue对象相同的方法之外,还具有
q.task_done()
使用者使用此方法发出信号,表示q.get()返回的项目已经被处理。
如果调用此方法的次数大于从队列中删除的项目数量,将引发ValueError异常。
q.join()
生产者将使用此方法进行阻塞,直到队列中所有项目均被处理。
阻塞将持续到为队列中的每个项目均调用q.task_done()方法为止。
下面的例子说明如何建立永远运行的进程,使用和处理队列上的项目。
生产者将项目放入队列,并等待它们被处理。
# JoinableQueue队列实现消费之生产者模型
from multiprocessing import Process, JoinableQueue
import time, random, os
def consumer(q):
while True:
res=q.get()
time.sleep(random.randint(1, 3))
print('\033[45m%s 吃 %s\033[0m' % (os.getpid(), res))
# 向q.join()发送一次信号,证明一个数据已经被取走了
q.task_done()
def producer(name,q):
for i in range(10):
time.sleep(random.randint(1,3))
res='%s%s' %(name,i)
q.put(res)
print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
# 生产完毕,使用此方法进行阻塞,直到队列中所有项目均被处理。
q.join()
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,))
c2=Process(target=consumer,args=(q,))
c1.daemon=True
c2.daemon=True
# 开始
p_l=[p1,p2,p3,c1,c2]
for p in p_l:
p.start()
p1.join()
p2.join()
p3.join()
print('主')
# 主进程等--->p1,p2,p3等---->c1,c2
# p1,p2,p3结束了,证明c1,c2肯定全都收完了p1,p2,p3发到队列的数据
# 因而c1,c2也没有存在的价值了,不需要继续阻塞在进程中影响主进程了。
# 应该随着主进程的结束而结束,所以设置成守护进程就可以了。
"""
"""
# 进程之间的数据共享
基于消息传递的并发编程是大势所趋,
但进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题。
以后我们会尝试使用数据库来解决现在进程之间的数据共享问题。
Manager模块介绍
进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的.
虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此.
Manager例子
from multiprocessing import Manager,Process,Lock
def work(d,lock):
with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
d['count']-=1
if __name__ == '__main__':
lock=Lock()
with Manager() as m:
dic=m.dict({'count':100})
p_l=[]
for i in range(100):
p=Process(target=work,args=(dic,lock))
p_l.append(p)
p.start()
for p in p_l:
p.join()
print(dic
"""
"""
进程池和multiprocess.Pool模块
定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,
等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。有很多任务需要执行,
池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行.
Pool([numprocess [,initializer [, initargs]]]):创建进程池
# 参数介绍:
1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组
# 主要方法
p.apply(func [, args [, kwargs]]):
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
需要强调的是:此操作并不会在所有池工作进程中并执行func函数。
如果要通过不同参数并发地执行func函数,
必须从不同线程调用p.apply()函数或者使用p.apply_async()
p.apply_async(func [, args [, kwargs]]):
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。
当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,
否则将接收其他异步操作中的结果。
p.close():
关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
P.jion():
等待所有工作进程退出。此方法只能在close()或teminate()之后调用
# 其他方法(了解)
方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达,
timeout是可选的。如果在指定时间内还没有到达,将引发一场。
如果远程操作中引发了异常,它将在调用此方法时再次被引发。
obj.ready():
如果调用完成,返回True
obj.successful():
如果调用完成且没有引发异常,返回True,
如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]):
等待结果变为可用。
obj.terminate():
立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。
如果p被垃圾回收,将自动调用此函数
# p.map进程池和进程效率测试--进程池和多进程效率对比
# 进程池的同步调用
import os,time
from multiprocessing import Pool
def work(n):
print('%s run' % os.getpid())
time.sleep(3)
return n**2
if __name__ == '__main__':
# 进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
p=Pool(3)
res_l=[]
for i in range(10):
# 同步调用,直到本次任务执行完毕拿到res,
# 等待任务work执行的过程中可能有阻塞也可能没有阻塞
# 但不管该任务是否存在阻塞,同步调用都会在原地等着
res=p.apply(work,args=(i,))
print(res_l)
# 进程池异步调用
import os
import time
import random
from multiprocessing import Pool
def work(n):
print('%s run' % os.getpid())
time.sleep(random.random())
return n**2
if __name__ == '__main__':
# 进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
p=Pool(3)
res_l=[]
for i in range(10):
# 异步运行,根据进程池中有的进程数,每次最多3个子进程在异步执行
# 返回结果之后,将结果放入列表,归还进程,之后再执行新的任务
# 需要注意的是,进程池中的三个进程不会同时开启或者同时结束
# 而是执行完一个就释放一个进程,这个进程就去接收新的任务。
res=p.apply_async(work,args=(i,))
res_l.append(res)
# 异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,
# 等待进程池内任务都处理完,然后可以用get收集结果
# 否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了
p.close()
p.join()
for res in res_l:
# 使用get来获取apply_aync的结果,如果是apply,则没有get方法,
# 因为apply是同步执行,立刻获取结果,也根本无需get
print(res.get())
# 进程池版并发聊天
# server端:
# Pool内的进程数默认是cpu核数,假设为4(查看方法os.cpu_count())
# 开启6个客户端,会发现2个客户端处于等待状态
# 在每个进程内查看pid,会发现pid使用为4个,即多个客户端公用4个进程
from socket import *
from multiprocessing import Pool
import os
server=socket(AF_INET, SOCK_STREAM)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
server.bind(('127.0.0.1', 8080))
server.listen(5)
def talk(conn):
print('进程pid: %s' %os.getpid())
while True:
try:
msg=conn.recv(1024)
if not msg:break
conn.send(msg.upper())
except Exception:
break
if __name__ == '__main__':
p=Pool(4)
while True:
conn,*_=server.accept()
p.apply_async(talk,args=(conn,))
# 同步的话,则同一时间只有一个客户端能访问
# p.apply(talk,args=(conn,client_addr))
# client端:
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))
# 使用多进程请求多个url来减少网络等待浪费的时间
# 爬虫实例
import re
from urllib.request import urlopen
from multiprocessing import Pool
def get_page(url,pattern):
response=urlopen(url).read().decode('utf-8')
return pattern,response
def parse_page(info):
pattern,page_content=info
res=re.findall(pattern,page_content)
for item in res:
dic={
'index':item[0].strip(),
'title':item[1].strip(),
'actor':item[2].strip(),
'time':item[3].strip(),
}
print(dic)
if __name__ == '__main__':
regex = r'<dd>.*?<.*?class="board-index.*?>(\d+)</i>.*?title="(.*?)".*?class="movie-item-info".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>'
pattern1=re.compile(regex,re.S)
url_dic={
'http://maoyan.com/board/7':pattern1,
}
p=Pool()
res_l=[]
for url,pattern in url_dic.items():
res=p.apply_async(get_page,args=(url,pattern),callback=parse_page)
res_l.append(res)
for i in res_l:
i.get()
"""
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
线程-- 能独立运行的基本单位——线程
进程的缺点:
1、进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了
2、进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,
即使进程中有些工作不依赖于输入的数据,也将无法执行。
进程是资源分配的最小单位,线程是CPU调度的最小单位.
每一个进程中至少有一个线程
进程和线程的区别:
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。
某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)
来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程操作系统中,进程不是一个可执行的实体。
关于进程线程协程的 操作系统可以归结为三点:
(1)以多进程形式,允许多个任务同时运行;
(2)以多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,
另一方面允许进程之间和线程之间共享资源
每个线程都是作为利用CPU的基本单位,是花费最小开销的实体,
线程的属性:
1)轻型实体;
线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源,
线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB控制
2)独立调度和分派的基本单位;
多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。
由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
3)共享进程资源;
线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:
所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;
此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。
由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
4)可并发执行;
在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;
同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。
注释:
TCB:
(1)线程状态。
(2)当线程不运行时,被保存的现场资源。
(3)一组执行堆栈。
(4)存放每个线程的局部变量主存区。
(5)访问同一个进程中的主存和其它资源。
用于指示被执行指令序列的程序计数器、保留局部变量、
少数状态参数和返回地址等的一组寄存器和堆栈
类似于进程,每个线程也有自己的堆栈,不同于进程,线程库无法利用时钟中断强制线程让出CPU,
可以调用thread_yield运行线程自动放弃cpu,让另外一个线程运行。
线程通常是有益的,但是带来了不小程序设计难度,线程的问题是:
1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程
2. 在同一个进程中,如果一个线程关闭了文件,而另外一个线程正准备往该文件内写内容呢?
因此,在多线程的代码中,需要更多的心思来设计程序的逻辑、保护程序的数据。
"""
"""
全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制.
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
Python提供用于多线程编程的模块,包括thread、threading和Queue等。
thread和threading模块允许程序员创建和管理线程。
thread模块提供了基本的线程和锁的支持,
threading提供了更高级别、功能更强的线程管理的功能。
Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,
"""
"""
threading模块
multiprocess模块的完全模仿了threading模块的接口,
二者在使用层面,有很大的相似性.
# 创建线程1
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi, args=('egon',))
t.start()
print('主线程')
# 创建线程2
from threading import Thread
import time
class Sayhi(Thread):
def __init__(self, name):
super().__init__()
self.name=name
def run(self):
time.sleep(2)
print('%s say hello' % self.name)
if __name__ == '__main__':
t = Sayhi('egon')
t.start()
print('主线程')
# 多线程与多进程
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello',os.getpid())
if __name__ == '__main__':
#part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
t1=Thread(target=work)
t2=Thread(target=work)
t1.start()
t2.start()
print('主线程/主进程pid',os.getpid())
#part2:开多个进程,每个进程都有不同的pid
p1=Process(target=work)
p2=Process(target=work)
p1.start()
p2.start()
print('主线程/主进程pid',os.getpid())
# 开启效率的比较
from threading import Thread
from multiprocessing import Process
import os
def work():
print('hello')
if __name__ == '__main__':
#在主进程下开启线程
t=Thread(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
hello
主线程/主进程
'''
#在主进程下开启子进程
t=Process(target=work)
t.start()
print('主线程/主进程')
'''
打印结果:
主线程/主进程
hello
'''
# 内存数据的共享问题
from threading import Thread
from multiprocessing import Process
import os
def work():
global n
n=0
if __name__ == '__main__':
# n=100
# p=Process(target=work)
# p.start()
# p.join()
# # 毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,
# # 查看父进程的n仍然为100
# print('主',n)
n=1
t=Thread(target=work)
t.start()
t.join()
# 查看结果为0,因为同一进程内的线程之间共享进程内的数据
print('主', n)
同一进程内的线程共享该进程的数据?
"""
"""
# 多线程实现socket
server端:
import multiprocessing
import threading
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('127.0.0.1',8080))
s.listen(5)
def action(conn):
while True:
data=conn.recv(1024)
print(data)
conn.send(data.upper())
if __name__ == '__main__':
while True:
conn,addr=s.accept()
p=threading.Thread(target=action,args=(conn,))
p.start()
client端:
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
s.send(msg.encode('utf-8'))
data=s.recv(1024)
print(data)
"""
"""
# Thread类的其他方法
Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。
正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,
与len(threading.enumerate())有相同的结果。
# 示例
from threading import Thread
import threading
from multiprocessing import Process
import os
def work():
import time
time.sleep(3)
print(threading.current_thread().getName())
if __name__ == '__main__':
#在主进程下开启线程
t=Thread(target=work)
t.start()
print(threading.current_thread().getName())
print(threading.current_thread()) #主线程
print(threading.enumerate()) #连同主线程在内有两个运行的线程
print(threading.active_count())
print('主线程/主进程')
'''
打印结果:
MainThread
<_MainThread(MainThread, started 140735268892672)>
[<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
主线程/主进程
Thread-1
'''
"""
"""
# join方法
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.start()
t.join()
print('主线程')
print(t.is_alive())
'''
egon say hello
主线程
False
'''
"""
"""
# 守护线程
无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。
需要强调的是:运行完毕并非终止运行。
1.对主进程来说,运行完毕指的是主进程代码运行完毕
2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,
主线程才算运行完毕
1.主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),
然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束;
2.主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。
因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,
而进程必须保证非守护线程都运行完毕后才能结束。
# 守护线程1
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.setDaemon(True) #必须在t.start()之前设置
t.start()
print('主线程')
print(t.is_alive())
'''
主线程
True
'''
# 守护线程2
from threading import Thread
import time
def foo():
print(123)
time.sleep(1)
print("end123")
def bar():
print(456)
time.sleep(3)
print("end456")
t1=Thread(target=foo)
t2=Thread(target=bar)
t1.daemon=True
t1.start()
t2.start()
print("main-------")
"""
"""
锁
# 同步锁
# 多个线程抢占资源的情况
from threading import Thread
import os,time
def work():
global n
temp=n
time.sleep(0.1)
n=temp-1
if __name__ == '__main__':
n=100
l=[]
for i in range(100):
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
print(n) #结果可能为99
import threading
R=threading.Lock()
R.acquire()
'''
对公共数据的操作
'''
R.release()
# 同步锁的引用
from threading import Thread,Lock
import os,time
def work():
global n
lock.acquire()
temp=n
time.sleep(0.1)
n=temp-1
lock.release()
if __name__ == '__main__':
lock=Lock()
n=100
l=[]
for i in range(100):
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
# 结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
print(n)
# 互斥锁与join的区别
# 死锁与递归锁
进程也有死锁与递归锁;
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,
因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
例如:
from threading import Lock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()
解决方法:递归锁,python为支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,
从而使得资源可以被多次require。直到一个线程所有的acquire都被release,
其他的线程才能获得资源。
例:
from threading import RLock as Lock
import time
mutexA=Lock()
mutexA.acquire()
mutexA.acquire()
print(123)
mutexA.release()
mutexA.release()
# 科学家吃面 死锁问题
import time
from threading import Thread,Lock
noodle_lock = Lock()
fork_lock = Lock()
def eat1(name):
noodle_lock.acquire()
print('%s 抢到了面条'%name)
fork_lock.acquire()
print('%s 抢到了叉子'%name)
print('%s 吃面'%name)
fork_lock.release()
noodle_lock.release()
def eat2(name):
fork_lock.acquire()
print('%s 抢到了叉子' % name)
time.sleep(1)
noodle_lock.acquire()
print('%s 抢到了面条' % name)
print('%s 吃面' % name)
noodle_lock.release()
fork_lock.release()
for name in ['哪吒','egon','yuan']:
t1 = Thread(target=eat1,args=(name,))
t2 = Thread(target=eat2,args=(name,))
t1.start()
t2.start()
# 递归锁解决死锁问题
import time
from threading import Thread,RLock
fork_lock = noodle_lock = RLock()
def eat1(name):
noodle_lock.acquire()
print('%s 抢到了面条'%name)
fork_lock.acquire()
print('%s 抢到了叉子'%name)
print('%s 吃面'%name)
fork_lock.release()
noodle_lock.release()
def eat2(name):
fork_lock.acquire()
print('%s 抢到了叉子' % name)
time.sleep(1)
noodle_lock.acquire()
print('%s 抢到了面条' % name)
print('%s 吃面' % name)
noodle_lock.release()
fork_lock.release()
for name in ['哪吒','egon','yuan']:
t1 = Thread(target=eat1,args=(name,))
t2 = Thread(target=eat2,args=(name,))
t1.start()
t2.start()
# 只用递归锁不用引起死锁 怎样?
"""
"""
# 线程队列
queue队列 :使用import queue,用法与进程Queue一样
# 先进先出class queue.Queue(maxsize=0) #先进先出
import queue
q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
first
second
third
'''
# 后进先出class queue.LifoQueue(maxsize=0) #last in fisrt out
import queue
q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')
print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
third
second
first
'''
# 存储数据时可设置优先级的队列class queue.PriorityQueue(maxsize=0)
import queue
q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))
print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
"""
"""
Python标准模块--concurrent.futures 启动并行任务
concurrent.futures模块提供了高度封装的异步调用接口
ThreadPoolExecutor:线程池,提供异步调用
ProcessPoolExecutor: 进程池,提供异步调用
#2 基本方法
#submit(fn, *args, **kwargs)
异步提交任务
#map(func, *iterables, timeout=None, chunksize=1)
取代for循环submit的操作
#shutdown(wait=True)
相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
#result(timeout=None)
取得结果
#add_done_callback(fn)
回调函数
# done()
判断某一个线程是否完成
# cancle()
取消某个任务
# ProcessPoolExecutor
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import os,time,random
def task(n):
print('%s is runing' %os.getpid())
time.sleep(random.randint(1,3))
return n**2
if __name__ == '__main__':
executor=ProcessPoolExecutor(max_workers=3)
futures=[]
for i in range(11):
future=executor.submit(task,i)
futures.append(future)
executor.shutdown(True)
print('+++>')
for future in futures:
print(future.result())
# ThreadPoolExecutor
# map 的用法
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
import os,time,random
def task(n):
print('%s is runing' %os.getpid())
time.sleep(random.randint(1,3))
return n**2
if __name__ == '__main__':
executor=ThreadPoolExecutor(max_workers=3)
# for i in range(11):
# future=executor.submit(task,i)
executor.map(task,range(1,12)) #map取代了for+submit
# 回调函数
"""
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
1、yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,
但是yield是代码级别控制的,更轻量级。
2、send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换
# 单纯的切换反而会降低效率
#串行执行
import time
def consumer(res):
'''任务1:接收数据,处理数据'''
pass
def producer():
'''任务2:生产数据'''
res=[]
for i in range(10000000):
res.append(i)
return res
start=time.time()
#串行执行
res=producer()
consumer(res) #写成consumer(producer())会降低执行效率
stop=time.time()
print(stop-start) #1.5536692142486572
#基于yield并发执行
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(10000000):
g.send(i)
start=time.time()
#基于yield保存状态,实现两个任务直接来回切换,即并发的效果
#PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的.
producer()
stop=time.time()
print(stop-start) #2.0272178649902344
# yield无法做到遇到io阻塞
import time
def consumer():
'''任务1:接收数据,处理数据'''
while True:
x=yield
def producer():
'''任务2:生产数据'''
g=consumer()
next(g)
for i in range(10000000):
g.send(i)
time.sleep(2)
start=time.time()
# 并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行
producer()
stop=time.time()
print(stop-start)
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,
以此来提升效率。
1、 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,
以便重新运行时,可以基于暂停的位置继续执行。
2、作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换
协程:是单线程下的并发,又称微线程;用户在单线程内控制协程的切换
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
1. python的线程属于内核级别的,即由操作系统控制调度
(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行。
2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,
以此来提升效率(!!!非io操作的切换与效率无关)。
协程的优点:
1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级。
2. 单线程内就可以实现并发的效果,最大限度地利用cpu
协程缺点:
1. 协程的本质是单线程下,无法利用多核,
可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程。
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结协程的特点:
1、必须在只有一个单线程里实现并发
2、修改共享数据不需加锁
3、用户程序里自己保存多个控制流的上下文栈
4、附加:一个协程遇到IO操作自动切换到其它协程
(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
"""
"""
Greenlet模块 安装 pip3 install greenlet
# greenlet实现状态切换
from greenlet import greenlet
def eat(name):
print('%s eat 1' %name)
g2.switch('egon')
print('%s eat 2' %name)
g2.switch()
def play(name):
print('%s play 1' %name)
g1.switch()
print('%s play 2' %name)
g1=greenlet(eat)
g2=greenlet(play)
# 可以在第一次switch时传入参数,以后都不需要
g1.switch('egon')
# 单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度
#顺序执行
import time
def f1():
res=1
for i in range(100000000):
res+=i
def f2():
res=1
for i in range(100000000):
res*=i
start=time.time()
f1()
f2()
stop=time.time()
print('run time is %s' %(stop-start)) #10.985628366470337
#切换
from greenlet import greenlet
import time
def f1():
res=1
for i in range(100000000):
res+=i
g2.switch()
def f2():
res=1
for i in range(100000000):
res*=i
g1.switch()
start=time.time()
g1=greenlet(f1)
g2=greenlet(f2)
g1.switch()
stop=time.time()
print('run time is %s' %(stop-start)) # 52.763017892837524
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,
那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题
"""
"""
单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,
我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率
Gevent模块 安装 pip install gevent
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,
gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程,
Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度.
# 用法介绍
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,
spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,
可以是位置实参或关键字实参,都是传给函数eat的
g2=gevent.spawn(func2)
g1.join() #等待g1结束
g2.join() #等待g2结束
#或者上述两步合作一步:gevent.joinall([g1,g2])
g1.value#拿到func1的返回值
# 遇到io主动切换
import gevent
def eat(name):
print('%s eat 1' %name)
gevent.sleep(2)
print('%s eat 2' %name)
def play(name):
print('%s play 1' %name)
gevent.sleep(1)
print('%s play 2' %name)
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('主')
# 要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头
from gevent import monkey;monkey.patch_all()
import gevent
import time
def eat():
print('eat food 1')
time.sleep(2)
print('eat food 2')
def play():
print('play 1')
time.sleep(1)
print('play 2')
g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print('主')
我们可以用threading.current_thread().getName()来查看每个g1和g2,
查看的结果为DummyThread-n,即假线程
from gevent import monkey;monkey.patch_all()
import threading
import gevent
import time
def eat():
print(threading.current_thread().getName())
print('eat food 1')
time.sleep(2)
print('eat food 2')
def play():
print(threading.current_thread().getName())
print('play 1')
time.sleep(1)
print('play 2')
g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print('主')
# Gevent之同步与异步
from gevent import spawn,joinall,monkey;monkey.patch_all()
import time
def task(pid):
# Some non-deterministic task
time.sleep(0.5)
print('Task %s done' % pid)
def synchronous(): # 同步
for i in range(10):
task(i)
def asynchronous(): # 异步
g_l=[spawn(task,i) for i in range(10)]
joinall(g_l)
print('DONE')
if __name__ == '__main__':
print('Synchronous:')
synchronous()
print('Asynchronous:')
asynchronous()
# 上面程序的重要部分是将task函数封装到Greenlet内部线程的gevent.spawn。
# 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,
# 后者阻塞当前流程,并执行所有给定的greenlet任务。
执行流程只会在 所有greenlet执行完后才会继续向下走.
"""
"""
# Gevent之应用举例
from gevent import monkey;monkey.patch_all()一定要放到导入socket模块之前,
否则gevent无法识别socket的阻塞
通过gevent实现单线程下的socket并发
server端:
from gevent import monkey;monkey.patch_all()
from socket import *
import gevent
#如果不想用money.patch_all()打补丁,可以用gevent自带的socket
# from gevent import socket
# s=socket.socket()
def server(server_ip,port):
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((server_ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr)
def talk(conn,addr):
try:
while True:
res=conn.recv(1024)
print('client %s:%s msg: %s' %(addr[0],addr[1],res))
conn.send(res.upper())
except Exception as e:
print(e)
finally:
conn.close()
if __name__ == '__main__':
server('127.0.0.1',8080)
client端:
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))
# 多线程并发多个客户端
from threading import Thread
from socket import *
import threading
def client(server_ip,port):
#套接字对象一定要加到函数内,即局部名称空间内,
# 放在函数外则被所有线程共享,则大家公用一个套接字对象,那么客户端端口永远一样了
c=socket(AF_INET,SOCK_STREAM)
c.connect((server_ip,port))
count=0
while True:
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))
msg=c.recv(1024)
print(msg.decode('utf-8'))
count+=1
if __name__ == '__main__':
for i in range(500):
t=Thread(target=client,args=('127.0.0.1',8080))
t.start()
"""
愿有更多的朋友,在网页笔记结构上分享更逻辑和易读的形式:
链接:暂无
提取码:暂无