Python实现并发编程

利用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函数的话会在主进程结束后统一清除。

Python实现并发编程_第1张图片

Python实现并发编程_第2张图片

结果如下:

Python实现并发编程_第3张图片
使用 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类对象,这会变得十分麻否。此时则使用进程池来创建进程

Python实现并发编程_第4张图片

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实现并发编程_第5张图片

Python demultiprocessing 模块包装了底层的机制,提供了 Queue(队列)、Pipes(管道)等多种方式来交换数据,接下来主要来看通过队列来实现进程间的通信

Python实现并发编程_第6张图片

队列常用方法:

队列的初始化:q = Queue(num)若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接收的消息数量没有上限(直到内存的尽头)。

Python实现并发编程_第7张图片

队列的使用方法:

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())

Python实现并发编程_第8张图片

进程之间的通信

Python实现并发编程_第9张图片

Python实现并发编程_第10张图片

Python实现并发编程_第11张图片

4.线程

线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每个线程并行执行不同的任务。

什么是线程?

线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。

线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所

拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行

Python 的标准库提供了两个模块:_thread和 threading,_thread 是低级模块,threading 是高级模块,对_thread 进行了封装。绝大多数情况下,我们只需要使用threading 这个模块。

Python实现并发编程_第12张图片

5.为什么要使用多线程?

线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄

和其他进程应有的状态。

因为线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程之中拥有独立的内存单元,而多个线程共享

内存,从而极大的提升了程序的运行效率。

线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享一个进程的虚拟空间。线程的共享环境

包括进程代码段、进程的共有数据等,利用这些共享的数据,线程之间很容易实现通信。

操作系统在创建进程时,必须为改进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程

来实现并发比使用多进程的性能高得要多。

总结起来,使用多线程编程具有如下几个优点:

进程之间不能共享内存,但线程之间共享内存非常容易。

操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高

python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。

你可能感兴趣的:(python)