Python signal(信号量)的使用
1、理解概念
(1)信号量一般用于处理资源互斥的情形,何为资源互斥,即只有一种资源,而多个线程要访问它。
举例:合租的时候,只有一个洗漱间,当大家都早上8点起床要去上班时,需要进行洗漱,
而这时候不可能都同时进行洗漱。因此,进去洗漱的人就把门锁了,
当洗完后,出来又打开门,后一个人就可以去洗漱,这个锁就可以理解为信号量。
后一个人看到信号量,即锁是开着的状态,就可以进去洗漱了。
(2)信号量,顾名思义,就是传递信号给别人的,从程序的层面看,一般就是线程之间互相通信。从而达到某个目的。
举例:楚云飞:今天不管谁在攻打平安县城,我三五八团一定帮帮场子
这里的李云龙攻打平安县城,可以理解为一个线程在工作,而楚云飞也可以理解为一个线程,信号量就是平安县城。
(3)使用场景一般就是某个线程工作完了,发送信号给另一个线程,告知后者可以进行某种操作
比如 kill -9 1234 就是告诉1234线程,你该溜了。简称杀死1234线程
又或者共用某个资源,用完后告知后者,我用完了,你可以用了。
2、为某个信号量绑定对应的事件程序接收到信号量时,会做对应的处理逻辑。
例如kill -9 pid就是发送信号9,杀死线程,而ctrl + c 就是发送信号2,中断线程。
一些信号量拥有自己的事件,比如信号9,信号2等,
当然,我们可以为每个信号绑定相应的事件。
示例:绑定信号1的事件
import datetime import os import signal import time def event(sig, frame): print('sig:{}'.format(sig)) # 1 print('type sig:{}'.format(type(sig))) # <class 'int'> print('frame:{}'.format(frame)) # <frame object at 0x7fbb81c235e8> print('type frame:{}'.format(type(frame))) # <class 'frame'> print('这是信号1的事件') if __name__ == '__main__': signal.signal(signal.ITIMER_VIRTUAL, event) # 绑定信号 1,函数为 event print('pid:{}'.format(os.getpid())) # 根据 pid 启动一个新控制台,发送信号1 命令 kill -1 pid while True: time.sleep(3) print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
运行程序,然后启动一个新的控制台,使用命名kill -1 pid 发送信号1, 此时程序接收到信号1,即主线程main接收到信号1 ,则进行信号1绑定的函数处理逻辑
我这里打印出的pid是5908,因此使用kill -1 5908 发送信号1测试
3、通过getsignal获取绑定信号量对应的事件
import signal def event(sig, frame): print(f'sig:{sig}') print('frame:{}'.format(frame)) if __name__ == '__main__': signal.signal(1, event) # 绑定信号 1,函数为 event for i in range(1, 10): # 查看1-10的信号各自都绑定了什么事件 function = signal.getsignal(i) # 使用 getsignal 查看信号绑定的事件 print(f'signal {i} -----> function {function}')
运行
4、常用信号量
查看编号可以去_signal.py中查看,可以写一个signal.ITIMER_VIRTURL然后点进去查看定义,即可。
4、绑定信号2(中断信号)为自定义函数
import datetime import os import signal import time def event(sig, frame): print(f'sig:{sig}') print('frame:{}'.format(frame)) if __name__ == '__main__': signal.signal(2, event) # 绑定信号 2 == ctrl + c ,函数为 event print('pid:{}'.format(os.getpid())) while True: time.sleep(3) print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
在控制台运行,然后按下ctrl + c,此时不会再中断程序,只会打印出^C,最后我们kill -9 pid即可结束程序
5、singal.alarm函数能给自己本身发送一个SIGALRM信号(定时器信号,值是14)
参数是秒,即多少秒后给自身线程发送定时器信号
import datetime import signal import sys import time def event(sig, frame): print('sig:{}'.format(sig)) print('type sig:{}'.format(type(sig))) # <class 'int'> print('frame:{}'.format(frame)) print('type frame:{}'.format(type(frame))) # <class 'frame'> sys.exit() # 接到定时器信号则退出 if __name__ == '__main__': signal.signal(signal.SIGALRM, event) # 绑定信号 14 == 定时器信号,函数为 event signal.alarm(7) # 7秒后发送定时器信号给自身 while True: time.sleep(3) print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
运行
6、设置绑定的函数为SIG_IGN,可以达到忽略信号的作用
示例:忽略中断信号
import datetime import os import signal import time if __name__ == '__main__': """ 信号2 == ctrl + c == 中断 一般情况下,当执行程序,按下ctrl + c 则可以结束该程序, 因为默认的2信号绑定的事件就是结束程序 当配置信号2为忽略信号时,即 signal.signal(2, signal.SIG_IGN) 则 ctrl + c 就不再可以中断信号了,该信号2被忽略了 可通过kill 9 pid 来进行杀死该进程,结束进程的运行 """ signal.signal(2, signal.SIG_IGN) # 忽略信号 2 == ctrl + c 正常ctrl + c 是中断,忽略后则不中断 print('pid:{}'.format(os.getpid())) while True: time.sleep(3) print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
运行同第4点
7、等待一个信号pause
import os import signal import time def event(sig, frame): print('sig:{}'.format(sig)) # 1 print('type sig:{}'.format(type(sig))) # <class 'int'> print('frame:{}'.format(frame)) # <frame object at 0x7fbb81c235e8> print('type frame:{}'.format(type(frame))) # <class 'frame'> print('这是信号1的事件') if __name__ == '__main__': signal.signal(signal.ITIMER_VIRTUAL, event) # 绑定信号 1,函数为 event print('pid:{}'.format(os.getpid())) # 根据 pid 启动一个新控制台,发送信号1 命令 kill -1 pid print('将要等待一个信号') signal.pause() # 等待一个信号 print('等待到信号了,继续往下执行') time.sleep(2) print('我要退出了')
运行启动程序,启动新的控制台,发送kill -1 pid信号
pause会等待接收一个信号,再继续往下执行
学习链接 : https://juejin.cn/post/6844903733466251272
https://docs.python.org/zh-cn/3/library/signal.html?highlight=signal