Python多进程和多线程的同步互斥

同步互斥机制:

  • 解决了多个进程或者线程对共享资源的争夺,因为某些共享资源在某个时刻可能只允许一个进程对其进行访问。

首先需要了解几个概念:

临界资源:

  • 临界资源就是上面提到的,同时只允许一个进程对其进行访问的资源,即:虽然是共享资源,但是不能多个进程同时对资源进行访问和操作。多进程之间需要遵守某些约定来对临界资源进行访问和操作。

临界区:

  • 临界区就是我们写的多进程代码中,对临界资源进行操作的那一部分代码段(区域)。

之前说了多进程之间需要遵守某些约定来对临界资源进行访问和操作,就是同步互斥

同步:

  • 同步可以理解为是一种合作关系,为了完成某项任务(访问临界资源),多个进程之间通过合作协调,排好顺序,依次操作。举个例子,在医院的一个诊室外排了很多看病的人,基于重症排在前,轻症排在后的规则排好了顺序,医生就可以看成是一个临界资源,医生每次只能看一个病人(每次只有一个病人(进程)能进诊室看医生(临界资源)),然后依次看完所有的病人。

互斥:

  • 互斥则是一种制约关系,进程访问临界资源时会上锁,使得其他进程阻塞等待,直到上一个进程退出后解锁,下一个进程才能够访问临界资源。还是上面的例子:在医院的一个诊室外有很多看病的人,并没有按照顺序排着,医生每次只能看一个病人,病人通过争夺抢占看谁先进入诊室,一个病人进入诊室后,就将诊室的门锁上(上锁),阻止其他病人进入,直到这个病人看完后,打开门出了诊室(解锁),剩下的病人又开始争夺抢占看谁先进入诊室。

Event事件

  • Event事件通常用于主进程控制其他进程的执行,Event事件主要提供了三个方法set、wait、clear。
  • 事件处理的机制:全局定义了一个标志,如果标志的值为 False,那么当程序执行到wait方法时就会阻塞。如果标志值设为True,那么wait方法便不再阻塞了。
  • Event事件对象的set方法将标志设为true,clear方法将标志设置为False,wait方法则是在标志值为False时阻塞等待。

主要方法:

e = Event()

  • 功能:创建事件对象

e.wait([timeout])

  • 功能:设置事件阻塞
  • 参数(timeout):阻塞等待时间

e.set()

  • 功能:事件设置,当事件被设置后e.wait()不再阻塞

e.clear()

  • 功能:清除设置,当事件设置被清除后e.wait又会阻塞

e.is_set()

  • 功能:事件状态判断
  • 返回值:True(wait方法未阻塞),False(wait方法阻塞)

代码实现:

from multiprocessing import Process,Event
from time import sleep

def test1():
    print("test1想操作文件")
    print("test11:", e.is_set())  # 判断事件状态
    e.wait()  # 设置事件阻塞
    print("test12:", e.is_set())  # 判断事件状态
    print("test2开始操作文件...")
    with open("test.txt") as f:
        print(f.read())
    e.clear()

def test2():
    print("test2也想操作文件")
    e.wait(2)  # 设置事件阻塞2秒
    if e.is_set():  # 判断事件状态
        with open("test.txt") as f:
            print(f.read())
    else:
        print("test2阻塞超时,不能读取文件")

# 创建事件对象
e = Event()
p1 = Process(target = test1)
p2 = Process(target = test2)
p1.start()
p2.start()

print("主进程创建文件")
with open('test.txt','w') as f:
    sleep(3)
    f.write("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
e.set()  # e.wait()不再阻塞
print('释放临界区使其不再阻塞')

p1.join()
p2.join()

该实例中,一个主进程和两个子进程对文件进行操作,在主进程创建文件并写入内容之前,子进程一直等待,其中一个p2子进程设置了阻塞超时时间,所以p2子进程会先执行完退出,而子进程p1会一直阻塞等待主进程调用set方法。

运行结果:

gk@gk-vm:~/python/test$ python3 process_event.py 
主进程创建文件
test1想操作文件
test11: False
test2也想操作文件
test2阻塞超时,不能读取文件
释放临界区使其不再阻塞
test12: True
test2开始操作文件...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Lock互斥锁

  • 互斥锁就是进程的互相排斥,谁先抢到资源,谁就上锁改资源内容。
  • 同一时间只允许一个进程上一把锁。

主要方法:
lock = Lock()

  • 功能:创建锁对象

lock.acquire()

  • 功能:上锁,如果锁已经是上锁状态调用此函数会阻塞

lock.release()

  • 功能:解锁

还有一种上锁方法

  • with lock

代码实现:

from multiprocessing import Process, Lock
import sys
from time import sleep

def test(n):
    lock.acquire()  # 上锁
    for i in range(3):
        print("test{}".format(n))
    lock.release()  # 解锁

# with lock上锁
# def test(n):
# 	with lock:
# 	    for i in range(3):
# 	        print("test{}".format(n))

# 创建锁对象
lock = Lock()

jobs = []
# 创建3个上锁的进程
for n in range(3):
	p = Process(target = test, args = (n,))
	p.start()
	jobs.append(p)

for j in jobs:
	j.join()

该实例创建了三个上锁的子进程。

运行结果:

gk@gk-vm:~/python/test$ python3 process_lock.py 
test0
test0
test0
test2
test2
test2
test1
test1
test1

观察结果发现三个子进程执行顺序是无序的,但是子进程之间并没有交叉执行,说明了上锁后同一时间只能执行一个进程,其他进程都会阻塞等待,而谁先上锁是没有顺序的,进程需要自己抢占资源,决定谁先执行。

另外:

  • Python多线程模块Thread中也有Event事件和Lock锁,用法和多进程的用法几乎一样。(注意区分多线程和多进程的原理)

你可能感兴趣的:(Python,多进程,多线程)