在 Python 中接管键盘中断信号的实现方法


Posted in Python onFebruary 04, 2020

在 Python 中接管键盘中断信号的实现方法

假设有这样一个需求,你需要从 Redis 中持续不断读取数据,并把这些数据写入到 MongoDB 中。你可能会这样写代码:

import json 
import redis 
import pymongo 
client = redis.Redis() 
handler = pymongo.MongoClient().example.col 
while True: 
  data_raw = client.blpop('data', timeout=300) 
  if not data_raw: 
    continue 
  data = json.loads(data_raw[1].decode()) 
  handler.insert_one(data)

但这样写有一个问题,就是每来一条数据都要连接一次 MongoDB,大量时间浪费在了网络 I/O上。

于是大家会把代码改成下面这样:

import json 
import redis 
import pymongo 
client = redis.Redis() 
handler = pymongo.MongoClient().example.col 
to_be_insert = [] 
while True: 
  data_raw = client.blpop('data', timeout=300) 
  if not data_raw: 
    continue 
  data = json.loads(data_raw[1].decode()) 
  to_be_insert.append(data) 
  if len(to_be_insert) >= 1000: 
    handler.insert_many(to_be_insert) 
    to_be_insert = []

每凑够1000条数据,批量写入到 MongoDB 中。

现在又面临另外一个问题。假设因为某种原因,我需要更新这个程序,于是我按下了键盘上的Ctrl + C强制关闭了这个程序。而此时to_be_insert列表里面有999条数据将会永久丢失——它们已经被从 Redis 中删除了,但又没有来得及写入 MongoDB 中。

我想实现,当我按下 Ctrl + C 时,程序不再从 Redis 中读取数据,但会先把to_be_insert中的数据(无论有几条)都插入 MongoDB 中。最后再关闭程序。

要实现这个需求,就必须在我们按下Ctrl + C时,程序还能继续运行一段代码。可问题是按下Ctrl + C时,程序就直接结束了,如何还能再运行一段代码?

实际上,当我们按下键盘上的Ctrl + C时,Python 收到一个名为SIGINT的信号。具体规则可以阅读官方文档。收到信号以后,Python 会调用一个信号回调函数。只不过默认的回调函数就是让程序抛出一个 KeyboardInterrupt异常导致程序关闭。现在,我们可以设法让 Python 使用我们自定义的一段函数来作为信号回调函数。

要使用信号,我们需用导入 Python 的signal库。然后自定义一个信号回调函数,当 Python 收到某个信号时,调用这个函数。

所以我们修改一下上面的代码:

import signal 
import json 
import redis 
import pymongo 
 
 
client = redis.Redis() 
handler = pymongo.MongoClient().example.col 
stop = False 
 
 
def keyboard_handler(signum, frame): 
  global stop 
  stop = True 
 
 
signal.signal(signal.SIGINT, keyboard_handler) 
 
to_be_insert = [] 
while not stop: 
  data_raw = client.blpop('data', timeout=300) 
  if not data_raw: 
    continue 
  data = json.loads(data_raw[1].decode()) 
  to_be_insert.append(data) 
  if len(to_be_insert) >= 1000: 
    handler.insert_many(to_be_insert) 
    to_be_insert = [] 
 
if to_be_insert: 
  handler.insert_many(to_be_insert)

我们定义了一个全局变量stop,默认为 False,所以默认情况下,while not stop所在的循环体会持续运行。

我们定义了一个函数keyboard_handler,它的作用是修改全局变量stop为 True。需要注意的是,在函数里面修改全局变量,必须先使用global 变量名声明这个变量为全局变量。否则无法修改。

修改以后,while not stop循环停止,于是程序进入:

if to_be_insert: 
  handler.insert_many(to_be_insert)

只要列表里面有数据,就会批量插入 MongoDB 中。然后程序结束。

整段代码的关键就在signal.signal(signal.SIGINT, keyboard_handler)这里把信号SIGINT与函数keyboard_handler关联上了,于是,在上面这段代码运行的任何时候,只要按下键盘的Ctrl + C,程序就会进入keyboard_handler函数里面,优先执行这个函数里面的代码。执行完成以后,回到之前中断的地方,继续执行之前没有完成的代码。而由于在函数里面我已经修改了stop的值,所以原来的循环不能继续执行,于是进入最后的收尾工作。

需要注意的是,如果你的整个代码全都是使用 Python 写的,那么 signal可以在你程序的任何阶段触发,只要你按下 Ctrl + C,立刻就会进入设置好的信号回调函数中。

但如果你的代码中,有一部分代码是使用 C 语言写的,那么当你按下Ctrl + C以后,可能需要等这段C 语言的代码运行完成以后,才会进入你设置的信号回调函数中。

总结

以上所述是小编给大家介绍的在 Python 中接管键盘中断信号的处理方法,希望对大家有所帮助!

Python 相关文章推荐
python高并发异步服务器核心库forkcore使用方法
Nov 26 Python
Python自动化运维和部署项目工具Fabric使用实例
Sep 18 Python
利用python画一颗心的方法示例
Jan 31 Python
python 调用c语言函数的方法
Sep 29 Python
浅谈python jieba分词模块的基本用法
Nov 09 Python
Python实现时钟显示效果思路详解
Apr 11 Python
python实现事件驱动
Nov 21 Python
梅尔倒谱系数(MFCC)实现
Jun 19 Python
python获取本周、上周、本月、上月及本季的时间代码实例
Sep 08 Python
如何Tkinter模块编写Python图形界面
Oct 14 Python
python状态机transitions库详解
Jun 02 Python
Python中np.random.randint()参数详解及用法实例
Sep 23 Python
在TensorFlow中屏蔽warning的方式
Feb 04 #Python
Python和Anaconda和Pycharm安装教程图文详解
Feb 04 #Python
Python3.7黑帽编程之病毒篇(基础篇)
Feb 04 #Python
python with (as)语句实例详解
Feb 04 #Python
Python实现实时数据采集新型冠状病毒数据实例
Feb 04 #Python
在tensorflow中实现屏蔽输出的log信息
Feb 04 #Python
Python变量作用域LEGB用法解析
Feb 04 #Python
You might like
PHP的FTP学习(一)[转自奥索]
2006/10/09 PHP
PHP生成及获取JSON文件的方法
2016/08/23 PHP
Yii框架防止sql注入,xss攻击与csrf攻击的方法
2016/10/18 PHP
用RadioButten或CheckBox实现div的显示与隐藏
2013/09/21 Javascript
JS实现模仿微博发布效果实例代码
2013/12/16 Javascript
jQuery中$.click()无效问题分析
2015/01/29 Javascript
JS实现仿google、百度搜索框输入信息智能提示的实现方法
2015/04/20 Javascript
详解JavaScript中的every()方法
2015/06/08 Javascript
js+html5实现canvas绘制网页时钟的方法
2016/05/21 Javascript
jquery  实现轮播图详解及实例代码
2016/10/12 Javascript
使用JS读取XML文件的方法
2016/11/25 Javascript
JavaScript实现经典排序算法之选择排序
2016/12/28 Javascript
jQuery实现的粘性滚动导航栏效果实例【附源码下载】
2017/10/19 jQuery
在vue项目中使用md5加密的方法
2018/09/14 Javascript
vue+webpack中配置ESLint
2018/11/07 Javascript
vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题
2020/04/14 Javascript
JS实现手写 forEach算法示例
2020/04/29 Javascript
微信小程序实现左滑删除效果
2020/11/18 Javascript
[04:40]2016国际邀请赛中国区预选赛全程TOP10镜头集锦
2016/07/01 DOTA
[00:55]深扒TI7聊天轮盘语音出处3
2017/05/11 DOTA
[42:32]Secret vs Optic 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
python中assert用法实例分析
2015/04/30 Python
python-itchat 获取微信群用户信息的实例
2019/02/21 Python
Python 函数list&read&seek详解
2019/08/28 Python
python使用smtplib模块发送邮件
2020/12/17 Python
美国快时尚彩妆品牌:Winky Lux(透明花瓣润唇膏)
2018/11/06 全球购物
印度在线购物网站:Paytmmall
2019/07/24 全球购物
单位成立周年感言
2014/01/26 职场文书
家长给孩子的评语
2014/01/30 职场文书
超市重阳节活动方案
2014/02/10 职场文书
优秀团干部个人事迹
2014/05/29 职场文书
党员教师一句话承诺
2014/05/30 职场文书
医院2014国庆节活动策划方案
2014/09/21 职场文书
春节随笔
2015/08/15 职场文书
Go语言使用select{}阻塞main函数介绍
2021/04/25 Golang
CSS基础详解
2021/10/16 HTML / CSS