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 

posted @ 2021-08-03 15:12  南风丶轻语  阅读(1945)  评论(0编辑  收藏  举报