python的信号量机制

博客围绕信号通信展开,介绍了Python发送信号、信号处理函数,还涉及信号量及其简单实现。同时阐述了进程的同步和互斥,如通过event事件实现父子进程读取文件不同部分,利用Lock锁互斥使用终端输出,以及在Linux下通过signal发送信号完成任务。
部署运行你感兴趣的模型镜像
  • 信号通信
一个进程向另一个进程发送一个信号来传递某种信息,接收者根据接收到的信号进行相应的行为

终端命令:
kill -l 查看系统信号
kill -sig PID 向一个进程发送信号

信号介绍:
信号名称		信号含义				默认处理方法
SIGHUP		连接断开
SIGINT		CTRL-C
SIGQUIT		CTRL-\
SIGTSTP		CTRL-Z
SIGKILL		终止一个进程
SIGSTOP		暂停一个进程
SIGALRM		时钟信号
SIGCHID	  子进程状态改变时给父进程发出、
  • python 发送信号
signal、os模块

os.kill(pid,sig)
	功能:发送信号
	参数: pid 目标进程 

signal.alarm(sec)
	功能:向自身发送时钟信号  --> SIGALRM
	参数:sec 时钟时间
* 进程中只能有一个时钟,第二个会覆盖第一个时间

同步执行:按照顺序逐句执行,一步完成再做下一步
异步执行:再执行过程中利用内核记录延迟发生或者准备处理的事件,这样不影响应用层的持续执行,当事件发生时再由内核告知应用层处理

* 信号是唯一的异步通信方法

signal.pause()
	功能:阻塞等待接收一个信号

signal.signal(signum,handler)
	功能:处理信号
	参数:signum 要处理的信号
		  handler 信号的处理方法:
		  	SIG_DFL 表示使用默认的方法处理
		  	SIG_IGN 表示忽略这个信号
		  	func 	传入一个函数,表示用指定函数处理
		  		函数格式要求:def func(sig,frame)
		  			sig: 捕获到的信号
		  			frame: 信号对象
  • 信号处理函数演示
from signal import *
import time

#信号处理函数
def handler(sig,frame):
	if sig == SIGALRM:
		print('接受到时钟信号')
	elif sig == SIGINT:
		print('接受到CTRL-C')

#def CTRL_C(sig,frame):
#	print('接收到CTRL-C')

alarm(5)

signal(SIGALRM,handler)
#signal(SIGINT,CTRL_C) 使用CTRL_C函数处理信号
signal(SIGINT,handler)

while True:
	time.sleep(2)
	print('Wating for a signal')
  • 信号量
原理:给定一个数量,对多个进程可见,且多个进程都可以操作。进程通过对数量多少的判断执行各自的行为。(生产者/消费者)

multiprocessing --> Semaphore()

sem = Semaphore(num)
	功能:创建信号量
	参数:信号量初始值
	返回:信号量对象

sem.get_value() 获取信号量值
sem.acquire() 将信号量减1  当信号量为0时会阻塞
sem.release() 将信号量加1
  • 信号量的简单实现
#演示信号量的创建和方法
from multiprocessing import Semaphore,Process
from time import sleep
import os

#创建信号量
sem = Semaphore(3)

def fun():
	print('进程{}等待信号量'.format(os.getpid()))

	#消耗一个信号量
	sem.acquire() # 为0则阻塞
	print('进程{}消耗一个信号量'.format(os.getpid()))
	sleep(3)

	sem.release()
	print('进程{}添加一个信号量'.format(os.getpid()))

jobs = []
for i in range(4):
	p = Process(target = fun)
	jobs.append(p)
	p.start()

for i in jobs:
	i.join()

print(sem.get_value())
  • 进程的同步和互斥
临界资源:多个进程后者线程都能够操作的共享资源
临界区:操作临界资源的代码段

同步:同步是一种合作关系,为完成某个任务,多进程或者多线程之间形成一种协调,按照约定或条件执行操作临界资源。

互斥:互斥是一种制约关系,当一个进程或者线程使用临界资源时进行上锁处理,当另一个进程使用时会阻塞等待,直到解锁后才能继续使用。

multiprocessing --> Event

创建事件对象
e = Event()

设置事件阻塞
e.wait([timeout])

事件设置 当事件被设置后e.wait()不再阻塞
e.set()

清除设置 当事件设置被clear后 e.wait又会阻塞
e.clear()

事件状态判断
e.is_set()
  • 通过event事件实现父子进程分别读取文件的前后部分
#使用父子进程分别将文件的前后两半部分读取到一个文件中
from multiprocessing import Process,Event
import os

#获取读文件的大小
size = os.path.getsize('file')

def son_process():
	print('子进程等待进入临界区')
	e.wait()
	
	fr = open('file','r')
	fw = open('file2','a') # 以追加方式打开文件
	n = size//2
	fr.seek(n,0) # 将游标设置到读文件的一半

	#读取后半部分文件
	while True:
		if n < 1024:
			data = fr.read(n)
			fw.write(data)
			break
		data = fr.read(1024)
		fw.write(data)
		n -= 1024

	fr.close()
	fw.close()
	print('子进程释放临界区')


e = Event()
p1 = Process(target = son_process)
p1.start()

#父进程读取文件的前半部分
fr = open('file','r')
fw = open('file2','w')

n = size // 2
while True:
	if n < 1024:
		data = fr.read(n)
		fw.write(data)
		break
	data = fr.read(1024)
	fw.write(data)
	n -= 1024

fw.close()
fr.close()
e.set()
p1.join()
  • Lock锁
创建对象
lock = Lock()

lock.acquire()  上锁,如果锁已经是上锁状态调用此函数会阻塞
lock.release()  解锁

with lock:  上锁
	......
	......
			 解锁
  • 通过上锁互斥使用终端输出
#演示锁互斥使用终端输出
from multiprocessing import Process,Lock
import sys

#两个进程都要进行锁的约束
def writer1():
	lock.acquire()
	for i in range(20):
		sys.stdout.write('Writer1想先向终端写入\n')
	lock.release()

def writer2():
	with lock:
		for i in range(20):
			sys.stdout.write('Writer2想先向终端写入\n')


lock = Lock()
w1 = Process(target = writer1)
w2 = Process(target = writer2)

w1.start()
w2.start()

w1.join()
w2.join()
  • 通过signal发送信号完成任务(linux下)
题目:
	司机和售票员的故事:
	× 创建父子进程分别代表司机和售票员
	× 当售票员收到SIGINT信号,给司机发送SIGUSR1信号此时司机打印'老司机发车了'
	× 当售票员收到SIGQUIT信号,给司机发送SIGUSR2信号此时司机打印'车速有点快,系好安全带'
	× 当司机捕捉到SIGTSTP信号,给售票员发送SIGUSR1,售票员打印'到站了,请下车'
	× 到站后,售票员先下车,司机下车(子进程先退出)

说明:SIGINT	SIGQUIT SIGTSTP从键盘发出
  • 代码:
# -*- coding:UTF-8 -*-
from multiprocessing import Process
import os
from signal import *
from time import sleep

def saler_handler(sig,frame):
	if sig == SIGINT:
		os.kill(os.getppid(),SIGUSR1)
	elif sig == SIGQUIT:
		os.kill(os.getppid(),SIGUSR2)
	elif sig == SIGUSR1:
		print('到站了,请下车')
		os._exit(0) # 子进程退出,可不写

#子进程代表售票员
def saler():
	signal(SIGINT,saler_handler)
	signal(SIGQUIT,saler_handler)
	signal(SIGUSR1,saler_handler)
	signal(SIGTSTP,SIG_IGN)
	while True:
		sleep(2)
		print('python带你去远方')

def driver_handler(sig,frame):
	if sig == SIGUSR1:
		print('老司机开车了')
	elif sig == SIGUSR2:
		print('车速有点快,系好安全带')
	elif sig == SIGTSTP:
		os.kill(p.pid,SIGUSR1)


p = Process(target = saler)
p.start()

#父进程
signal(SIGUSR1,driver_handler)
signal(SIGUSR2,driver_handler)
signal(SIGTSTP,driver_handler)
signal(SIGINT,SIG_IGN)
signal(SIGQUIT,SIG_IGN)

p.join()

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

### 信号量机制的运用习题与解析 #### 示例一:仓库管理问题 在多进程环境下,一个仓库可以存储两种产品(A和B),分别最多能存储M个A产品和N个B产品。两个进程分别负责生产A产品和B产品,并将它们存入仓库。为了防止仓库超载或同时访问冲突,使用信号量来控制生产者的行为。 代码实现如下: ```python from threading import Semaphore # 初始化信号量 mutex = Semaphore(1) # 实现互斥访问仓库 Sa = Semaphore(M - 1) # 控制A产品的剩余存储空间 Sb = Semaphore(N - 1) # 控制B产品的剩余存储空间 def process_A(): while True: # 生产A产品 produce_A() Sa.acquire() # 检查是否还有空间存放A产品[^1] mutex.acquire() # 确保只有一个进程访问仓库 store_A() # 将A产品存入仓库 mutex.release() Sb.release() # 唤醒等待的B产品生产者 def process_B(): while True: # 生产B产品 produce_B() Sb.acquire() # 检查是否还有空间存放B产品[^1] mutex.acquire() # 确保只有一个进程访问仓库 store_B() # 将B产品存入仓库 mutex.release() Sa.release() # 唤醒等待的A产品生产者 ``` #### 示例二:交通路口计数系统 某交通路口设置了一个自动计数系统,由“观察者”进程和“报告者”进程组成。观察者进程识别并计数通过的卡车,报告者进程定时打印当前计数值并将计数清零。为避免并发访问冲突,使用信号量进行同步。 代码实现如下: ```python from threading import Semaphore # 初始化信号量和变量 S = Semaphore(1) # 控制对共享变量count的访问 count = 0 # 卡车计数器 def observer(): global count while True: observe_truck() # 观察到一辆卡车通过 S.acquire() # 获取信号量以确保互斥访问 count += 1 # 更新卡车计数 S.release() def reporter(): global count while True: wait_for_hour() # 等待一小时 S.acquire() # 获取信号量以确保互斥访问 print(count) # 打印当前计数值 count = 0 # 清零计数器 S.release() ``` #### 示例三:线程顺序输出问题 创建三个线程,分别输出字符`a`、`b`和`c`,要求按顺序输出`abcabc...`。使用信号量来控制线程的执行顺序。 代码实现如下: ```python from threading import Thread, Semaphore # 初始化信号量 sem_a = Semaphore(1) # 线程1开始时可用 sem_b = Semaphore(0) # 线程2等待线程1完成 sem_c = Semaphore(0) # 线程3等待线程2完成 def thread_a(): while True: sem_a.acquire() # 等待信号量 print('a', end='') # 输出字符a sem_b.release() # 唤醒线程2 def thread_b(): while True: sem_b.acquire() # 等待信号量 print('b', end='') # 输出字符b sem_c.release() # 唤醒线程3 def thread_c(): while True: sem_c.acquire() # 等待信号量 print('c', end='') # 输出字符c sem_a.release() # 唤醒线程1 # 创建并启动线程 Thread(target=thread_a).start() Thread(target=thread_b).start() Thread(target=thread_c).start() ``` #### 示例四:读者-写者问题 在一个多线程环境中,多个进程需要访问共享资源(如文件)。允许多个读者同时读取数据,但不允许写者与读者或写者之间同时访问资源。使用信号量解决此问题。 代码实现如下: ```python from threading import Semaphore # 初始化信号量 mutex = Semaphore(1) # 控制对共享资源的互斥访问 read_count = 0 # 当前读者数量 room_empty = Semaphore(1) # 写者进入房间的条件 def reader(): global read_count room_empty.acquire() # 确保房间为空[^2] read_count += 1 if read_count == 1: # 如果是第一个读者,则阻止写者进入 mutex.acquire() room_empty.release() # 进行读操作 read_data() room_empty.acquire() read_count -= 1 if read_count == 0: # 如果没有读者,则允许写者进入 mutex.release() room_empty.release() def writer(): room_empty.acquire() # 确保房间为空[^2] write_data() # 进行写操作 room_empty.release() ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值