文章目录
本篇开始分析Python中的并发程序,也就是进程、线程、协程序的使用。由于是用自己的语言总结的,因此比较大白话,或者叫通俗易懂。而且很多细节方面没有具体介绍,因为在Python进程管理中很少用到,这里主要分析的就是比较重要的点
基本概念
1、操作系统的本质
帮助我们控制硬件、管理软件的软件,对下提供了一个我们控制计算机的接口,对上帮助我们管理应用软件
2、早期的批处理系统
人机交互过程太多,太过浪费时间和资源,如下图:
3、解决批处理系统的问题
(1)SPOOLING技术
卡片被拿到机房后能够很快的将作业从卡片读入磁盘,于是任何时刻当一个作业结束时,操作系统就能将一个作业从磁带读出,装进空出啦的内存区域运行,这种技术叫做同时的外部设备联机操作:SPOOLING该技术同时用于输出。当采用了这种技术后,就不在需要IBM1401机了,也不必将磁带搬来搬去了(中间俩小人失业了),强化了操作系统的功能。
(2)多道程序设计,用于解决顺序执行的问题
在7094机上(程序运行的机器),若当前作业因等待磁带或等待其他IO操作而暂停,CPU就处于休闲状态直至IO操作完成,对于CPU密集的科学计算,IO操作少,浪费时间不明显,对于商业数据处理,IO等待能到达80%~90%,所以必须解决CPU浪费的现象。
4、分时操作系统
分时操作系统=多个联机终端+多道技术
由于许多程序员怀念第一代独享的计算机,可以即时调试自己的程序。为了满足程序员们很快可以得到响应,出现了分时操作系统。
20个客户端同时加载到内存,有17在思考,3个在运行,cpu就采用多道的方式处理内存中的这3个程序,由于客户提交的一般
都是简短的指令而且很少有耗时长的,索引计算机能够为许多用户提供快速的交互式服务,所有的用户都以为自己独享了计算机资源
5、进程的本质
(1)本质:一个程序在一个数据集上的一次动态执行过程
(2)电脑可以多个程序同时运行,如一边听着音乐,一边用QQ聊天;一个程序的运行可能有多个进程,如多个QQ同时在线,一个;我们在资源管理器里可以看到,某个软件有很多个进程。QQ进行多窗口聊天并截图。
(3)但其实它们并不是同时进行的,因为一个电脑只有一个CPU,而一个CPU不可能满足多个程序同时运行,之所以给我们看到它们是同时运行,是因为CPU处理速度太快了,可以达到纳秒(10的-9次方)级别,它运行程序时就可以瞬间切换,一会儿运行这个一会儿运行那个,当然,它们都是按照规则来运行。虽然一直在切换,由于速度特别快,因此给我们感觉是CPU在同时运行多个程序,一种并发效果。
(4)不过,虽然它能达到一种并发运行程序的效果,但是这也是有极限的,这就是为什么当我们运行的程序太多的时候就会出现卡顿的原因之一。到现在,我们已经有了双核、四核、八核CPU,就相当于有多个CPU同时转,让我们的电脑或手机更加流畅,说白了就是我们更加无法察觉出它运行程序的瞬间切换。
6、线程的本质
(1)本质:线程是进程的一个最小执行单元,进程之下有多个线程,一个进程至少有一个线程。
(2)如某个软件下有多个功能,而我们运行某一项功能就是打开一个线程,而该软件运行着,就是一个大的进程。如在QQ里面边聊天,同时接受邮件,同时使用截图。
(3)进程与线程的区别:进程是最小的资源单位,系统分配内存是分给进程的;线程是最小的执行单元,但不可能只有线程存在,因为它需要进程的资源这个前提。
threading线程模块
1、开三个线程
(1)参考代码:
import threading #导入线程模块
import time
def Hi(num):
print('hello python %d\n' %num)
time.sleep(3) #睡3秒
if __name__ == '__main__':
t1 = threading.Thread(target=Hi,args=(10,))
#实例化一个线程对象;传入一个函数名,不能加();再传入函数需要的参数到args中
t1.start() #启动一个线程对象
t1 = threading.Thread(target=Hi,args=(9,)) #线程二
t1.start() #启动一个线程对象
print('运行结束!')
(2)运行原理:
(3)参考结果:
2、线程对象的 join 方法
就是等待效果,对子线程使用
(1)不加join的情况:
import threading
import time
def game():
print('%s开始玩游戏...' %time.ctime())
time.sleep(3)
print('%s结束停止玩游戏。' %time.ctime())
def music():
print('\n%s开始播放音乐...'%time.ctime())
time.sleep(5)
print('%s结束播放音乐。' %time.ctime())
if __name__ == '__main__':
t1 = threading.Thread(target=game) #实例化线程对象
t1.start() #执行线程对象
t2 = threading.Thread(target=music) #实例化线程对象
t2.start() #执行线程对象
#效果:开始玩游戏、开始播放音乐、程序结束先出来;
#再停顿3秒,出停止玩游戏,再停2秒,出结束播放,程序一共执行5秒
print('\n程序结束!')
(2)不加join结果:
(3)将if name == ‘main’:的内容换成以下:
if __name__ == '__main__':
t1 = threading.Thread(target=game) #实例化线程对象
t1.start() #执行线程对象
t2 = threading.Thread(target=music) #实例化线程对象
t2.start() #执行线程对象
t1.join() #加了一个join就会按照顺序来执行,等子线程执行完了再回到主线程
#t2.join()
print('\n程序结束!')
(4)加join的结果:
(5)若将join处的语句改成
t1.join() #加了一个join就会按照顺序来执行,等子线程执行完了再回到主线程
#t2.join()
则会先出两个开始,睡3秒,再出停止游戏,和程序结束,等2秒,出接收播放音乐
3、继承式调用
import threading
import time
class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num
def run(self):#定义每个线程要运行的函数
print("running on number:%s" %self.num)
time.sleep(3)
if __name__ == '__main__':
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
print("ending......")
继承式调用的用处并不大,也非常少用到,这里只简单介绍一下
4、setDamen()守护线程
将子线程跟随主线程的结束而结束,就是一旦回到主线程,子线程的后续动作都不会执行了一定要放在start之前用
参考代码:
import threading
import time
threads = [] #用来装子线程的空列表
def game():
print('\n%s开始玩游戏...' %time.ctime())
time.sleep(3)
print('%s结束停止玩游戏。' %time.ctime())
def prograss():
print('\n%s start to write prograss...' %time.ctime())
time.sleep(5)
print('%s finish to write prograss.' %time.ctime())
#实例化2个线程对象
t1 = threading.Thread(target=game)
t2 = threading.Thread(target=prograss)
#追加2个对象到列表中
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
t1.setDaemon(True)
for t in threads:
#t.setDaemon(True) #必须放在start之前
t.start()
print('\n程序结束!%s' %time.ctime())
5、threading下的其他方法
run(): 线程被cpu调度后自动执行线程对象的run方法
start(): 启动线程活动
isAlive(): 返回线程是否活动的
getName(): 返回线程名
setName(): 设置线程名
threading.currentThread(): 返回当前的线程变量
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果