Python系统编程笔记
01. 进程与程序
编写完毕的代码,在没有运行的时候,称之为程序
正在运行着的代码,就称为进程
进程是系统分配资源的最小单位。
进程资源包括:
中间变量
代码
计数器
02. 通过os.fork()函数在程序中创建进程
示例:
import os
import time
ret = os.fork() # 创建新的进程 一次调用,两次返回
if ret == 0:
for i in range(3):
print("放音乐")
time.sleep(0.1)
else:
for i in range(3):
print("跳舞")
time.sleep(0.1)
说明:
1. 程序执行到os.fork()时,操作系统会创建一个新的进程(子进程),然后复制父进程的所有信息到子进程中
2. 然后父进程和子进程都会从fork()函数中得到一个返回值,在子进程中这个值一定是0,而父进程中是子进程的pid号
3. 该函数为操作系统提供只能在Unix/Linux操作系统中使用,而不能在windows中使用。
03. 获取进程编号
1. Python中的方法
os.getpid() 获取当前进程的pid
os.getppid() 获取当前进程的父进程的pid
2. Linux中的方法
ps -aux 获取当前进程的pid
ps -ef 可以查看到父进程的pid
04. 资源回收
01分配给子进程的资源由父进程负责回收
倘若子进程没有运行完,父进程运行结束退出,留下的子进程被称为孤儿进程。父进程死掉后,孤儿进程会被别的进程收养,通常是init进程(pid为1),资源会被继父进程回收,所以没什么危害.
倘若子进程先运行完,而父进程并没有回收资源,那么这个子进程就被称为僵尸进程。僵尸进程会造成资源浪费。
02. 父进程回收子进程资源的方法
pid, result = os.wait()
返回值pid表示回收的子进程的pid
result表示子进程的退出状态,通常0表示正常运行完退出
该函数是阻塞方式运行,直到子进程运行结束
05. 多个进程修改全局变量的问题
由于子进程的资源来源于父进程的拷贝,所以父子进程都分配有自己的存储空间,在进程修改全局变量,并不会影响另一个进程内变量的值。
06. 通过Process类创建进程
01. 介绍
Process类位于multiprocessing模块中,是Python封装好的可以跨平台的类。
02. 创建对象
p = Process(target=fn, args=(),kwargs={},name="")
target参数表示创建的进程要执行的函数
args参数表示fn函数所需要的参数
kwargs表示fn函数所需要的关键字参数
name参数为当前创建的进程起一个别名,可以通过p.name访问得到
03. Process对象常见的属性和方法
p.name 获取进程的别名
p.pid 获取进程的pid
p.start() 真正创建出子进程,启动子进程
p.is_alive() 查看子进程是否存活,返回True或者False
p.terminate() 无论子进程是否执行完,立即终止。存在延迟,这是因为子进程创建后就作为一个单独的进程,要想在父进程中结束,需要父进程告诉操作系统,操作系统在下次调度子进程时结束掉子进程
p.join() 回收子进程的资源,阻塞,指导子进程执行完
04. 示例
from multiprocessing import Process
import os
import time
def sub_process_fun(num, a):
for i in range(10):
print("子进程:hello")
time.sleep(0.1)
def main():
p = Process(target=sub_process_fun, args=(100,), kwargs={"a": 200}) # 创建一个子进程对象
p.start() # 真正的创建出子进程,子进程可以开始执行代码
p.join() # 回收子进程资源 阻塞
if __name__ == '__main__':
main()
07. 通过继承Process类创建进程
创建一个类继承自Process类,然后把子进程要执行的任务放在run方法中即可。
示例:
class SubProcess(Process):
def __init__(self, num, a):
super(SubProcess, self).__init__() # 执行父类Process默认的初始化方法,通过父类的方法,将子进程对象初始化好
self.num = num
self.a = a
def run(self):
"""子进程要执行的代码"""
print("子进程:pid=%d" % os.getpid())
print("子进程:num=%d" % self.num)
print("子进程:a=%d" % self.a)
for i in range(10):
print("子进程:hello")
time.sleep(0.1)
def main():
p = SubProcess(100, 200)
p.start() # 真正的创建出子进程,子进程可以开始执行代码
time.sleep(0.1)
p.terminate() # 终止子进程的执行 存在延迟
p.join() # 回收子进程资源 阻塞
if __name__ == '__main__':
main()
08. 进程池
01. 介绍
当程序中需要大量创建进程时就可以使用进程池来维护这些进程
进程池中会创建指定数量的进程,每次交给进程池任务后,进程池会
自动选择一个空闲的进程来运行交给的任务。
Python中的进程池通过使用multiprocessing模块的Pool类来实现
02. 创建进程池对象
pool = Pool(3)
参数3表示创建的进程池中最大进程数
03. 进程池对象的常见方法和属性
01. pool.apply_async(fn, args, callback)
用空闲出来的子进程去调用目标fn,非阻塞
fn 表示需要进程执行的任务函数
args 表示fn函数的参数,是一个元组
callback 异步任务结束后会自动调用callback参数对应的函数,同时把异步任务fn的返回值传递给callback的参数。callback是由主进程调用的。
02. pool.apply(fn, args)
用空闲出来的子进程去调用目标fn,阻塞
03. pool.close()
关闭进程池,关闭后po不再接收新的请求
04. pool.join()
等待po中所有子进程执行完成,必须放在close语句之后,阻塞方式
04. 注意点
应该先定义任务,然后定义进程池对象。这是因为定义进程池对象的时候会
创建子进程,同时把父进程的资源复制一份给子进程,倘若此时任务函数还
没有定义,子进程在调用函数的时候就会出现找不到的情况。
09. 异步 同步
同步: 从多任务的角度出发,多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线。即子线程执行的时候主线程会停止等待。
异步: 从多任务的角度出发,多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线。即主线程和子线程可以同时执行。
阻塞: 从调用者的角度出发,如果在调用的时候,被卡住,不能再继续向下运行,需要等待,就说是阻塞
非阻塞: 从调用者的角度出发, 如果在调用的时候,没有被卡住,能够继续向下运行,无需等待,就说是非阻塞
同步和异步是从整个过程中间是否有等待的角度来看
阻塞和非阻塞是从拿到结果之前的状态的角度来看。
10. Queue类
01. 介绍
multiprocessing类的Queue类,提供队列的功能,该功能由操作系统提供,由Python进行封装而成
Queue对象不能当做一个普通的变量来看待,由于它是由操作系统提供,数据并不保存在进程内部,进程内的变量只是该对象的引用,所以可以实现多个进程间的通信。
02. 使用
1. q = Queue(3) #
构造一个Queue对象,3表示可以向队列中put3条消息,如果不传参数,则表示可以put任意条消息。
2. q.put(obj, timeout=3)
往队列中添加数据。如果没有timeout参数,并且队列已经满了,则会出现阻塞直到队列中有空闲位置。
timeout表示最长等待时间。如果队列中数据已满,则最多等待3秒,如果3秒内不能添加数据,则抛出异常
3. obj = q.get(timeout=n)
从队列中取数据,如果队列中没有数据,则等待n秒,n秒内没有取到数据,则会抛出异常
如果没有timeout参数,则会阻塞在这里一直等待直到队列中有数据。
4. q.task_done() 一般配合get使用,让队列计数减1
4. q.put_nowait()
向队列中添加数据,如果队列已满,则立即抛出异常
5. obj = q.get_nowait()
从队列中取数据,如果队列中没有数据,则立即抛出异常
6. q.full()
查看队列是否已满 返回True或者FALSE
7. q.empty()
查看队列是否是空的。返回True或者False
8. q.qsize()
当前队列中数据的个数。mac中不支持。
9. q.join()
多线程中,让主线程等待队列中的事情做完然后再退出。
03. 通过Queue对象实现进程间通讯
queue = Queue(3) # 参数表明这个队列最多只能保存3条数据
def process_write():
for i in range(3):
queue.put(i)
time.sleep(0.5)
def process_read():
while not queue.empty():
ret = queue.get()
time.sleep(0.1)
p1 = Process(target=process_write)
p2 = Process(target=process_read)
p1.start()
p1.join()
p2.start()
p2.join()
04. 进程池中进程之间的通讯,不能使用Queue类,而是使用multiprocessing模块提供的Manager类
对象的Queue()方法来创建一个队列对象。其他使用规则一样。
11. 线程介绍
可以简单理解为同一进程中有多个计数器
线程内每个线程的执行时间不确定,而每个进程的时间片相等
线程是操作系统调度执行的最小单位
12. 进程线程的区别与优缺点
1. 定义的不同
进程是系统进行资源分配的最小单位.
线程是进程的一个实体,是CPU进行调度的基本单位,
它是比进程更小的能独立运行的基本单位.
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),
但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
2. 区别
一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
线线程不能够独立执行,必须依存在进程中
3. 优缺点
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。
13. 多线程的实现
通过threading模块的Thread类来实现多线程
1. 通过实例化一个Thread类的一个对象来实现
2. 通过继承Thread类来实现
3. 实现的所有方法都可Process类的一样,可以参考Process类
14. 获取线程信息
通过threading模块的enumerate()方法在主线程中获取所有线程信息
返回一个列表,每一个元素是一个线程对象,保存一个线程的基本信息
threading.current_thread()
获取当前线程信息
15. 线程的几种状态
就绪状态: 创建一个线程对象,调用start方法后,线程就处于就绪状态
运行状态: 操作系统调度该线程时,线程就处于运行状态。
等待(阻塞): 当线程运行中遇到阻塞,操作系统就会立即切换到别的线程去执行,同时这个线程进入阻塞状态,等待阻塞结束后,重新进入就绪状态,等待操作系统下次调度。所以线程的执行时间不均等。
死亡状态: 线程调度运行结束后进入死亡状态
16. 注意点
对于通过Thread类或者Process类实现的多线程或者多进程,
如果主线程(进程)没有执行join操作,则主线程(进程)执行完也会
等待子线程(进程)执行完后一块退出。
这是由于Python封装实现的,而底层的os.walk()则不会这样
17. 多线程修改全局变量
由于多个线程共享同一进程内的资源,
所以多线程修改全局变量会出现资源竞争的问题。
18. 使用互斥锁解决多线程资源竞争问题
1. 通过使用threading模块的Lock类来实现
2. Lock类对象的使用方法
1. mutex = Lock()
创建一个互斥锁对象
2. mutex.acquire(blocking=True, timeout=None)
给互斥锁上锁
blocking表示是以阻塞或者非阻塞的方式运行上锁
True 默认 以阻塞的方式运行
首先判断当前互斥锁的状态,
如果是上锁状态,则阻塞等到锁为未上锁状态,由于发生阻塞,操作系统会立即切换到别的线程执行
如果是未上锁状态,则将互斥锁设置为上锁状态
总是返回True
如果指定了timeout参数,则表示最长等待时间,超过这个时间仍未上锁成功,就不再等待,直接返回一个false值
FALSE 表示以非阻塞方式运行该方法
运行该方法会直接返回结果,如果上锁成功,则返回True,上锁失败,返回FALSE
注:
非阻塞方式可以通过对返回值的判断而知道是否上锁成功,这样就可以在上锁不成功的情况
下做一些其他的事情而不用总是等待在这里。
3. mutex.release()
释放锁
将互斥锁的状态设置为打开。
19. 多线程修改局部变量
通过继承Thread类得到一个子类,
然后通过该子类构造多个对象,让这几个对象同时开启子线程
或者定义一个函数,多个线程同时运行该函数,
通过在函数内修改函数的局部变量或者子类的run方法内修改对象的属性,
多个线程直接不会互相影响修改的结果。
20. 线程死锁
1. 起因
线程中存在多个互斥锁,多个线程都需要对方的资源即每个线程都需要对方的锁先打开,就会出现死锁现象
2. 避免死锁的方法
1. 添加超时时间
给acquire方法添加timeout参数,过了超时时间还拿不到锁,就把自己的互斥锁释放掉
2. 银行家算法
该算法是从资源分配者的角度出发,
先把资源分配给需求最少的客户,等这个客户用完把所有资源归还后,
再分配给需求大一点的客户
21. 同步应用(多个线程按照一定的顺序执行)
通过互斥锁来控制线程的运行顺序
即一个线程运行完并不释放自己的互斥锁,
而是释放下一个线程要用到的互斥锁,
下一个线程就会得到互斥锁,
执行完后也不释放自己的互斥锁,同样释放下一个想要执行的线程的互斥锁,
最后一个线程释放第一个线程的锁。
程序开始时,把第一个要执行的线程的互斥锁打开,其他的全部上锁。
22. 生产者消费者模式
所谓生产者消费者模式,放在线程中就是一个线程用来产生数据,
而另一个线程用来处理数据。同时为了减少这两个线程之间的耦合度,
可以引入一个仓库,生产者和消费者都操作这个仓库。
仓库可以使用queue模块的Queue类来模拟,使用方法和multiprocessing模块的Queue类一样。
23. GIL(全局解释器锁)
全局解释器锁导致Python中的多线程并不能同时运行
在Python中创建一个子线程相当于在Python解释器的进程
中创建一个线程,而所有的代码要运行都需要用到解释器的一些全局资源,
而为了让每个线程都能安全的运行,就出现了GIL,每个线程或者每段代码在运行的时候都需要拿到GIL才能运行,
这就让Python的多线程并不是真正的多个线程在同时执行,而是交替进行的。
当然这里所说的子线程或者代码是指计算型的代码,涉及到io型的代码并不需要拿到GIL就可以执行,因为io操作一般都是调用底层C语言的代码,
调用之前就会自动释放掉GIL锁,这个时候其他的线程就可以拿到GIL来运行
所以在涉及IO操作的部分,可以使用多线程来提高效率。
GIL问题只有在C语言实现的cpython解释器中有,别的版本的Python解释器如pypy,jpython中并没有这个问题。
24. 多线程多进程的使用地方
一般CPU密集型即需要大量计算的时候考虑使用多进程以充分利用CPU
IO密集型即文件读写,网络访问,数据库访问时考虑使用多线程。
Python系统编程笔记的更多相关文章
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
- python核心编程--笔记(不定时跟新)(转)
的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v ...
- Python 异步编程笔记:asyncio
个人笔记,不保证正确. 虽然说看到很多人不看好 asyncio,但是这个东西还是必须学的.. 基于协程的异步,在很多语言中都有,学会了 Python 的,就一通百通. 一.生成器 generator ...
- python核心编程笔记(转)
解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v 冗 ...
- python socket编程笔记
用python实现一个简单的socket网络聊天通讯 (Linux --py2.7平台与windows--py3.6平台) 人生苦短之我用Python篇(socket编程) python之路 sock ...
- Python之系统编程笔记
概念 命令行工具. Shell 脚本. 系统管理 系统模块 sys 提供一组功能映射Python运行时的操作系统 os 提供跨平台可移植的操作系统编程接口 os.path 提供文件及目 ...
- python核心编程笔记——Chapter7
Chapter7.映像和集合类型 最近临到期末,真的被各种复习,各种大作业缠住,想想已经荒废了python的学习1个月了.现在失去了昔日对python的触觉和要写简洁优雅代码的感觉,所以临到期末毅然继 ...
- Python核心编程笔记(类)
Python并不强求你以面向对象的方式编程(与Java不同) # coding=utf8 class FooClass(object): version = 0.1 def __init__(self ...
- Python核心编程笔记--动态属性
一.动态语言与静态语言 1.1 静态语言特点: a. 在定义变量时需要指定变量的类型,根据指定的类型来确定变量所占的内存空间 b. 需要经过编译才能运行 c. 在代码编译后,运行过程不能对代码进行操作 ...
随机推荐
- iOS项目的目录结构(Cocoa China)
目录结构 AppDelegate Models Macro General Helpers Vendors Sections Resources 一个合理的目录结构首先应该是清晰的,让人一眼看上去 ...
- python_程序模拟浏览器请求及会话保持
python下读取一个页面的数据可以通过urllib2轻松实现请求 import urllib2 print urllib2.urlopen('http://www.baidu.com').read( ...
- EF5+MVC4系列(11)在主视图中用Html.RenderPartial调用分部视图(ViewDate传值);在主视图中按钮用ajax调用子action并在子action中使用return PartialView返回分布视图(return view ,return PartialView区别)
一:主视图中使用Html.RenderPartial来调用子视图(注意,这里是直接调用子视图,而没有去调用子Action ) 在没有使用母版页的主视图中(也就是设置了layout为null的视图中), ...
- IE8不支持数组的indexOf方法
在IE8下有个js错误,但是在其它浏览器下(Firefox, Chrome, IE9)下面都很正常.后来调试发现原因是在IE8下,js数组没有indexOf方法. 在使用indexOf方法前,执行一下 ...
- ASM实例原始磁盘搜索路径
discovery diskstring==>ASM实例原始磁盘搜索路径,一般搜索/dev/raw/ /dev/oracleasm/ 初始化参数文件中为:asm_diskstring asmc ...
- Logback中文文档(四):Appender
什么是 Appender Appender是负责写记录事件的组件.Appender 必须实现接口"ch.qos.logback.core.Appender".该接口的重要方法总结如 ...
- SOA及分布式
结合领域驱动设计的SOA分布式软件架构 Windows平台分布式架构实践 - 负载均衡(下) 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载 我终于深入参与了一 ...
- java 对象数组
java 对象数组 from zhaocundang@163.com 先 用类声明数组: 再把类的实例赋给数组: package works; import java.util.Scanner; pu ...
- asp.net单击头模板中的checkbox,实现datalist中所有chebox的全选和取消
转载时请以超链接形式标明文章原始出处和作者信息及本声明http://blueseach.blogbus.com/logs/31281126.html 使用C#和javascript都可以实现,第二种更 ...
- kafka学习之-雅虎开源管理工具Kafka Manager
http://blog.csdn.net/lizhitao/article/details/44523663