如果多个进程抢占去对某一个共同的对象的操作的时候,为了防止操作产生冲突,这个时候就可以通过锁的操作来避免。

案例展示

以下展示没有使用锁的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
from multiprocessing import Process,Value,Array


def test_func(n,):
n.value = n.value + 1

if __name__ == "__main__":
num = Value('i',0)
for i in range(20):
p = Process(target=test_func,args=(num,))
p.start()

time.sleep(1)
print(num.value)

当改变time.sleep()的时间,就会发现每一次打印的结果都不一样,是因为多个进程在操作同一个对象的原因,在实际的工作当中,多个进程操作同一个对象是非常危险的时候,很有可能导致数据错乱或者数据不同步的情况发生。但是有两种情况列外,就是在操作队列与管道的时候,这两种数据结构由于自身的性质,只能排队被操作,因此不用担心可能出现的错误。

想要保证操作对象时候数据不会被其他进程串改,需要保证在一个进程操作的时候,其他进程不能够访问该对象,即当前进程锁住该对象。操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from multiprocessing import Process
from multiprocessing import Lock


def for_fun(lock):
lock.acquire() # 加锁
pass
lock.release() # 释放锁


if __name__ == '__main__':
lock = Lock()
p = Process(target=for_fun(), args=(lock, ))

进程池

为什么要有进程池的概念?

在实际的需求当中,往往需要创建多个进程,但是进程也不能够随意创建,进程太多不方便管理的同时还会降低效率,因此诞生了进程池,进程池这个对象维护了一个进程管理器,在这个进程池当中,进程是恒定的,被事先设置过。进程池会稳定维护四个进程,并且当任务传递过来后,进程池会接纳所有任务并且让任务等待也存在于进程池当中,不会因为进程不够用而阻塞程序的正常运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
import time
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor


def task(num):
print("执行",num)
time.sleep(2)


if __name__ == "__main__":
pool = ProcessPoolExecutor(4)
for ele in range(10):
pool.submit(task,ele)

在进程池当中,进程都被进程池管理,如果想要进程在执行完毕后再做其他操作可以使用回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
"""进程池 回调函数 回调函数会把任务函数的结果作为参数执行"""
from multiprocessing import Pool
import time
import os


def work():
time.sleep(1)
print("%d 进程 work" % os.getpid())
return time.time()


def call(res):
# time.sleep(1)
print("处理回调结果:{}".format(str(res)))


if __name__ == '__main__':
pool = Pool(2)
for i in range(5):
# 在进程池中的每个任务执行完毕时,执行回调函数
# 使用callback回调函数时,会把工作任务func方法的返回结果传给callback方法进行处理
res = pool.apply_async(func=work, callback=call)
pool.close() # 必须等进程池关闭后,才能使用join()方法
pool.join() # 主进程等待子进程结束
print("main end")

注意:如果进程池当中要使用进程锁。则需要基于Manager钟的Lock和RLock来实现。

这篇写得草率了,唉!