利用python实现并发编程的方法有:
1.多线程
2.多进程
3.多线程+多进程
面试题:进程和线程的区别和联系?
进程 -操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程
线程 -操作系统分配CPU的基本单位
并发编程(concurrentprogramming)
1. 提升执行性能- 让程序中没有因果关系的部分可以并发的执行
2. 改善用户体验- 让耗时间的操作不会造成程序的假死
进程:计算机中已经运行程序的实体
每一个进程都有一个不重复的进程ID号,简称PID,对进程进行表示
multiprocessing 模块提供了一个process类来代表一个进程对象
os.getpid()可获取当前进程id,返回值为int
os.getppid()可获取父进程id,返回值为int
join方法是主进程等待调用join方法的子进程终止
主要参数:
Process([group [, target [, name [, args [, kwargs]]]]])
group:参数未使用,为None
target:表示当前进程启动时执行的可调用对象
name:为当前进程实例的别名
args:表示传递给target函数的参数元组
kwargs:表示传递给target函数的参数字典
Process类常见的属性以及方法:
name:当前进程实例的别名,一般为Process-N
pid:当前进程实例的PID
is_alive():判断进程示实例是否在运行
start():启动进程实例
join([timeout]):是否等待进程实例执行结束,或者等待多少秒
terminate:不管任务是否完成,立即终止
1.注意:Process对象可以创建进程,但Process对象不是进程,其删除与否与系统资源是否被回收没有直接的关系。
2.主进程执行完后会默认等待子进程结束后回收资源,不需要手动回收资源;join()函数用来控制子进程结束的顺序,其内部也有一个清除僵尸进程的函数,可以回收资源;
3.Process进程创建时,子进程会将主进程的Process对象完全复制一份,这样在主进程和子进程各有一个Process对象,但是p.start()启动的是子进程,主进程中的Process对象作为一个静态对象存在,不执行。
4.当子进程执行完毕后,会产生一个僵尸进程,其会被join函数回收,或者再有一条进程开启,start函数也会回收僵尸进程,所以不一定需要写join函数。
5.windows系统在子进程结束后会立即自动清除子进程的Process对象,而linux系统子进程的Process对象如果没有join函数和start函数的话会在主进程结束后统一清除。
结果如下:
使用 Process 子类创建进程
定义一个类来继承Process类,如果要使用多进程时,则仅仅需要实例化这个类。
class SubProcess(Process):
def __init__(self, interval, name=''):
Process.__init__(self) # 继承Process类
self.interval = interval
if name:
self.name = name
def run(self):
print("子进程为{}开始执行,父进程为{}开始执行".format(os.getpid(), os.getppid()))
start = time.time()
time.sleep(self.interval)
end = time.time()
print("子进程{}结束,耗时{:.2f}s".format(os.getpid(), end-start))
if __name__ == "__main__":
print("父进程开始执行")
print("父进程PID={}".format(os.getpid()))
p1 = SubProcess(interval=1, name='test')
p2 = SubProcess(interval=2)
p1.start()
p2.start()
print("p1 is_alive={}".format(p1.is_alive()))
print("p2 is_alive={}".format(p2.is_alive()))
print("p1 name={}".format(p1.name))
print("p2 name={}".format(p2.name))
print("p1 pid={}".format(p1.pid))
print("p2 pid={}".format(p2.pid))
如果要处理复杂任务的进程,通常定义一个类,使其继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象
如果要创建成百上千个进程,则需要实例化成百上千个Process类对象,这会变得十分麻否。此时则使用进程池来创建进程
deftask(name):
print("子进程{}开始执行task{}".format(os.getpid(), name))
time.sleep(1)
if__name__ == "__main__":
print("父进程开始执行")
print("父进程的PID={}".format(os.getpid()))
p = Pool(3) # 最大进程数为3
for i in range(10):
p.apply_async(task,args=(i, )) # 非阻塞方式调用task
print("等待所有子进程结束")
p.close() # 关闭进程池
p.join() # 主进程等待调用join的子进程终止
print("父进程执行结束")
上述例子中,最大进程数为 3,且以非阻塞方式调用 task(),代表着同一时间有3个进程在调用 task() 方法,3个进程结束之后再开启3个进程,结束之后再开启3个,最后只有1个进程在执行。
重点:每个进程都有自己的地址空间、内存、数据栈以及其他记录其运行状态的辅助数据,进程之间没有共享信息。(以下代码验证了这句话)
defplus():
print("加法开始执行")
global g_num
g_num += 50
print('g_num is %d' % g_num)
print("加法进程结束")
defminus():
print("减法开始执行")
global g_num
g_num -= 50
print('g_num is %d' % g_num)
print("---减法进程执行结束---")
结果:
Python demultiprocessing 模块包装了底层的机制,提供了 Queue(队列)、Pipes(管道)等多种方式来交换数据,接下来主要来看通过队列来实现进程间的通信
队列常用方法:
队列的初始化:q = Queue(num)若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接收的消息数量没有上限(直到内存的尽头)。
队列的使用方法:
q = Queue(3) # 初始化一个队列对象,最多可以接受三条put消息
q.put('new1')
q.put('new2')
print(q.full()) # 如果队列满了就返回True,否则返回false
q.put('new3')
print(q.full())
try:
q.put('new4', True, 2)
except:
print("消息数量已满,现有消息数量为:{}".format(q.qsize()))
try:
q.put_nowait('new4')
except:
print("消息数量已满,现有消息数量为:{}".format(q.qsize()))
if not q.empty():
print("从队列中获取消息")
for i in range(q.qsize()):
print(q.get_nowait())
进程之间的通信
线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每个线程并行执行不同的任务。
什么是线程?
线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。
线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所
拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行
Python 的标准库提供了两个模块:_thread和 threading,_thread 是低级模块,threading 是高级模块,对_thread 进行了封装。绝大多数情况下,我们只需要使用threading 这个模块。
线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄
和其他进程应有的状态。
因为线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程之中拥有独立的内存单元,而多个线程共享
内存,从而极大的提升了程序的运行效率。
线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享一个进程的虚拟空间。线程的共享环境
包括进程代码段、进程的共有数据等,利用这些共享的数据,线程之间很容易实现通信。
操作系统在创建进程时,必须为改进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程
来实现并发比使用多进程的性能高得要多。
总结起来,使用多线程编程具有如下几个优点:
进程之间不能共享内存,但线程之间共享内存非常容易。
操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高
python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。