二十一、python多进程
发布日期:2021-10-31 07:31:28 浏览次数:15 分类:技术文章

本文共 6869 字,大约阅读时间需要 22 分钟。

进程简介

进程是程序在计算机上的一次执行活动。当你运行一个程序,你就启动了一个进程。显然,程序是死的(静态的),进程是活的(动态的)。进程可以分为系统进程和用户进程。凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;用户进程就不必我多讲了吧,所有由你启动的进程都是用户进程。进程是操作系统进行资源分配的单位。

在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。但实际上在任何一个时间内有且仅有一个进程占有CPU。

进程和线程

进程(process)和线程(thread) 单个CPU一次只能运行一个任务;在任一时刻,CPU总是运行一个进程,其他进程处于非运行状态;

一个进程可以包含多个线程; 进程没有任何共享状态,进程修改的数据,改动仅限于该进程内;
一个进程的内存空间是共享的,每个线程都可以使用这些共享内存;
一个线程使用某些共享内存时,其他线程必须等它结束才能使用这一块内存;防止多个线程同时读写某一块内存区域,采用互斥锁(Mutual
exclusion,缩写Mutex);
某些内存区域只能供给固定数目的线程使用,此时通过信号量(Semaphore)保证多个线程不会互相冲突;
多进程形式,运行多个任务同时运行;多线程形式,允许单个任务分成不同的部分运行; 多线程使用的是cpu的一个核,适合io密集型;
多进程使用的是cpu的多个核,适合运算密集型。

在linux中可以使用ps -efL查看进程和线程ID。以memcached进程为例,输出结果如下

ps -efL |grep memcachedroot     24421     1 24421  0   10 May19 ?        00:00:03 memcached -d -u rootroot     24421     1 24422  0   10 May19 ?        00:00:01 memcached -d -u rootroot     24421     1 24423  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24424  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24425  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24426  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24427  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24428  0   10 May19 ?        00:00:00 memcached -d -u rootroot     24421     1 24429  0   10 May19 ?        00:00:09 memcached -d -u rootroot     24421     1 24430  0   10 May19 ?        00:00:00 memcached -d -u rootroot     32169 31101 32169  0    1 23:23 pts/0    00:00:00 grep --color=auto memcached

第一行UID(用户ID),第二行为PID(进程ID),第三行PPID(父进程ID),第四行LWP(线程ID)。

从示例可以看出,进程24421子进程有10个,对应线程ID分别为24421-24430。

multiprocess

python中的多线程无法利用多核优势,若要充分使用多核CPU资源,在python中大部分情况使用多进程。python提供了非常好用的多进程包multiprocessing。

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

Process类

创建一个Process对象

p = multiprocessing.Process(target=worker_1, args=(2, ))

参数

  • target:函数名字
  • args:函数需要的参数,以tuple的形式传入(单个元素的tuple必须有逗号)

方法

p.is_alive() 判断进程p是否存活,是返回True

p.run() 启动进程,它去调用target指定的函数
p.start() 启动进程,它会自动调用run方法,推荐使用start
p.join(timeout) 主线程等待p终止(主线程处于等的状态,p处于运行状态)。p.join只能join使用start开启的进程,不能join使用run开启的

进程

p.terminate() 强制进程p退出,不会进行任何清理操作,如果p创建了子进程,该子进程就变成了僵尸进程

属性

p.name 进程的名字

p.pid 进程的pid
p.daemon 默认为False,如果设置为True代表p为后台运行的守护进程,当p的父进程终止时p也随之终止,并且设置为True后,p不能创建自己的新进程,必须在p.start()之前设置

import multiprocessingimport timedef worker(args, interval):    print("start worker {0}".format(args))    time.sleep(interval)    print("end worker {0}".format(args))def main():    print("start main")    p1 = multiprocessing.Process(target=worker, args=(1, 1))    p2 = multiprocessing.Process(target=worker, args=(2, 2))    p3 = multiprocessing.Process(target=worker, args=(3, 3))    p1.start()    p2.start()    p3.start()    print("end main")if __name__ == '__main__':    main()输出结果start mainend mainstart worker 1start worker 2start worker 3end worker 1end worker 2end worker 3

multprocessing用到的两个方法

cpu_count():统计cpu总数
active_children():获得所有子进程

import multiprocessingimport timedef worker(args, interval):    print("start worker {0}".format(args))    time.sleep(interval)    print("end worker {0}".format(args))def main():    print("start main")    p1 = multiprocessing.Process(target=worker, args=(1, 1))    p2 = multiprocessing.Process(target=worker, args=(2, 2))    p3 = multiprocessing.Process(target=worker, args=(3, 3))    p1.start()    p1.join(timeout=0.5)    #此处保证了p1优先执行    p2.start()    p3.start()    print("the number of CPU is: {0}".format(multiprocessing.cpu_count()))    for p in multiprocessing.active_children():       print("The name of active children is: {0}, pid is: {1} is alive".format(p.name, p.pid))    print("end main")if __name__ == '__main__':    main()输出结果start mainstart worker 1the number of CPU is: 4The name of active children is: Process-1, pid is: 25360 is aliveThe name of active children is: Process-2, pid is: 24500 is aliveThe name of active children is: Process-3, pid is: 26100 is aliveend mainstart worker 3start worker 2end worker 1end worker 2end worker 3

lock组件

当我们用多进程来读写文件的时候,如果一个进程是写文件,一个进程是读文件,如果两个文件同时进行,肯定是不行的,必须是文件写结束以后,才可以进行读操作。或者是多个进程在共享一些资源的时候,同时只能有一个进程进行访问,那就要有一个锁机制进行控制。

下面使用2个进程分别进行+1和+3操作为例

不加锁

import timeimport multiprocessingdef add(value, number):    print("start add{0} number= {1}".format(value, number))    for i in range(1, 3):        number += value        time.sleep(0.3)        print("number = {0}".format(number))if __name__ == '__main__':    print("start main")    number = 0    p1 = multiprocessing.Process(target=add, args=(1, number))    p3 = multiprocessing.Process(target=add, args=(3, number))    p1.start()    p3.start()    print("end main")输出结果start mainend mainstart add1 number= 0start add3 number= 0number = 1number = 3number = 2number = 6

加锁

import timeimport multiprocessingdef add(lock, value, number):    with lock:        print("start add{0} number= {1}".format(value, number))        for i in range(1, 3):            number += value            time.sleep(0.3)            print("number = {0}".format(number))if __name__ == '__main__':    print("start main")    number = 0    lock = multiprocessing.Lock()    p1 = multiprocessing.Process(target=add, args=(lock, 1, number))    p3 = multiprocessing.Process(target=add, args=(lock, 3, number))    p1.start()    p3.start()    print("end main")输出结果start mainend mainstart add1 number= 0number = 1number = 2start add3 number= 0number = 3number = 6

锁的获取可以使用lock.acquire()获取,lock.release()释放

def add(lock, value, number):    lock.acquire()    print("start add3 number= {0}".format(number))    try:        for i in range(1, 5):            number += value            time.sleep(0.3)            print("number = {0}".format(number))    except Exception as e:        raise e    finally:        lock.release()        pass

共享内存

一般变量在进程之间是没法进行通讯的,但是multiprocessing提供了Value和Array模块,可以在不同的进程中使用同一变量。Value和Array结构内部都实现了锁机制,因此多进程是安全的。

Value和Array都需要设置其中存放值的类型,d是double类型,i是int类型。类型设置和array模块的值类似,更多的类型可以点击array — Efficient arrays of numeric values查看。
上面的示例中,两个进程执行后number结果分别为2和6,假如两个进程可以共享变量,name输出结果将会是8。

import multiprocessingfrom multiprocessing import Valuedef add(value, number):    print("start add{0} number= {1}".format(value, number.value))    for i in range(1, 3):        number.value += value        print("number = {0}".format(number.value))if __name__ == '__main__':    print("start main")    number = Value('d', 0)    #使用Value创建变量    p1 = multiprocessing.Process(target=add, args=(1, number))    p3 = multiprocessing.Process(target=add, args=(3, number))    p1.start()    p3.start()    print("end main")输出结果start mainend mainstart add1 number= 0.0start add3 number= 0.0number = 1.0number = 4.0number = 5.0number = 8.0number最终结果是8,但是具体输出结果每次执行可能存在差异。

转载地址:https://blog.csdn.net/weixin_39934221/article/details/80388015 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:二十二、多线程
下一篇:二十、python访问memcached

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年03月23日 14时02分10秒