python 进程与线程
1、进程的定义
进程就是一个程序在一个数据集上的一次动态执行过程。
进程一般由程序、数据集、进程控制块三部分组成。
程序用来描述进程要完成哪些功能以及如何完成;
数据集则是程序在执行过程中所需要使用的资源;
进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
2、线程
线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。
3、进程和线程之间的关系
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
或者说进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程则是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
来个总结修快快:
进程和线程的关系:
# (1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 # (2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 # (3)CPU分给线程,即真正在CPU上运行的是线程。
# 并行处理(Parallel Processing)是计算机系统中能同时执行两个或更多个处理的一种计算方法。 # 并行处理可同时工作于同一程序的不同方面。 # 并行处理的主要目的是节省大型和复杂问题的解决时间。 # # # 并发处理(concurrency Processing):指一个时间段中有几个程序都处于已启动运行到运行完毕之间, # 且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机(CPU)上运行 # 并发的关键是你有处理多个任务的能力,不一定要同时。 # 并行的关键是你有同时处理多个任务的能力。
所以说,并行是并发的子集
# 为什么要用进程:想同一时间运行多个程序,但是切换开销太大了
# 为什么要用线程:解决了开销大的问题
# 并行:进程并行,和多核相关,一定要多cpu,否则一定是并发
# 并发:和多核无关,单核内线程并发
# 进程:最小的资源管理单位(盛放线程的容器)
# 线程:最小的执行单位
4、同步与异步:
同步:同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步:异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
举个例子,打电话时就是同步通信,发短息时就是异步通信。
5、介绍一个有关线程的模块,threading
搞一个线程出来:
import threading import time def listen(): print("listen") time.sleep(3) print("listen over") def writeblog(): print("writeblog") time.sleep(5) print("writeblog over") print(time.time()-s) s=time.time() # 线程对象 t1=threading.Thread(target=listen) t2=threading.Thread(target=writeblog) t1.start() t2.start() print("ending")
用继承一个线程类的写法:
1 #继承Thread式创建 2 3 import threading 4 import time 5 6 class MyThread(threading.Thread): 7 8 def __init__(self,num): 9 threading.Thread.__init__(self) 10 self.num=num 11 12 def run(self): 13 print("running on number:%s" %self.num) 14 # 睡三秒程序结束 15 time.sleep(3) 16 17 t1=MyThread(56) 18 t2=MyThread(78) 19 20 t1.start() 21 t2.start() 22 print("ending")
先思考打印什么,然后代码验证下:养成头壳中运行代码的习惯
6、threading类的实例都有哪些方法
# join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。 # 换句话解释:要等绑定这个方法的对象执行完毕才可以执行主线程。 # setDaemon(True): ''' 将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。 当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行。 那么当主线程完成,想退出时,会检验子线程是否完成。 如果子线程未完成,则主线程会等待子线程完成后再退出。 但是有时候我们需要的是只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以 用setDaemon方法 '''
import threading from time import ctime,sleep import time def Music(name): print ("Begin listening to {name}. {time}".format(name=name,time=ctime())) sleep(3) print("end listening {time}".format(time=ctime())) def Blog(title): print ("Begin recording the {title}. {time}".format(title=title,time=ctime())) sleep(5) print('end recording {time}'.format(time=ctime())) threads = [] t1 = threading.Thread(target=Music,args=('FILL ME',)) t2 = threading.Thread(target=Blog,args=('hello',)) threads.append(t1) threads.append(t2) if __name__ == '__main__': #t2.setDaemon(True) for t in threads: #t.setDaemon(True) #注意:一定在start之前设置 t.start() t.join() #等价于必须某程序结束以后才能执行主程序 #t1.join() #阻塞主线程,等t1结束,然后进行主线程 #t2.join() # 考虑这三种join位置下的结果? print ("all over %s" %ctime())
现在的结果是:
Begin listening to FILL ME. Mon May 8 14:58:00 2017
end listening Mon May 8 14:58:03 2017
Begin recording the . Mon May 8 14:58:03 2017
end recording Mon May 8 14:58:08 2017
all over Mon May 8 14:58:08 2017
其他方法:
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
7、GIL(global interpreter lock):全局解释器锁
# python 的多线程败笔: # 多进程可以并行,然而由于GIL锁的缘故,导致同一时刻,同一进程,只能有一个线程运行。 # 多线程不可进行并行。 # # 并行是最快的,并发速度稍微慢点 # join:等待当前线程完成,才执行主线程 # 守护线程:跟随主线程,主线程结束,子线程直接结束,步调和主线程保持一致 # gil # 计算密集型:大量使用cpu且占据,python多线程不好使 # IO密集型:存在大量IO操作,python多线程好使 # python 使用多核: # 用进程弊端:开销大,切换复杂 # 解决方案:协程 本质上是单线程 # # # 方向:IO多路复用 # 终极思路:换C模块实现多线程