在 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 相关文章推荐
精确查找PHP WEBSHELL木马的方法(1)
Apr 12 Python
python创建和删除目录的方法
Apr 29 Python
在arcgis使用python脚本进行字段计算时是如何解决中文问题的
Oct 18 Python
Python实现将数据库一键导出为Excel表格的实例
Dec 30 Python
浅述python中argsort()函数的实例用法
Mar 30 Python
Python中音频处理库pydub的使用教程
Jun 07 Python
django 2.0更新的10条注意事项总结
Jan 05 Python
python pandas 对时间序列文件处理的实例
Jun 22 Python
Django中使用极验Geetest滑动验证码过程解析
Jul 31 Python
python 生成器和迭代器的原理解析
Oct 12 Python
Python3.7 基于 pycryptodome 的AES加密解密、RSA加密解密、加签验签
Dec 04 Python
python中sys模块的介绍与实例
Apr 17 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
第1次亲密接触PHP5(2)
2006/10/09 PHP
PHP 万年历实现代码
2012/10/18 PHP
解决PHP mysql_query执行超时(Fatal error: Maximum execution time …)
2013/07/03 PHP
Dwz与thinkphp整合下的数据导出到Excel实例
2014/12/04 PHP
thinkphp微信开发(消息加密解密)
2015/12/02 PHP
Yii2验证器(Validator)用法分析
2016/07/23 PHP
thinkphp框架无限级栏目的排序功能实现方法示例
2020/03/29 PHP
php+websocket 实现的聊天室功能详解
2020/05/27 PHP
event.srcElement 用法笔记e.target
2009/12/18 Javascript
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
js中的异常处理try...catch使用介绍
2013/09/21 Javascript
Node.js的特点和应用场景介绍
2014/11/04 Javascript
跟我学习javascript的prototype,getPrototypeOf和__proto__
2015/11/17 Javascript
jQuery解析json格式数据简单实例
2016/01/22 Javascript
JavaScript中的原型继承基础学习教程
2016/05/06 Javascript
jquery购物车结算功能实现方法
2020/10/29 Javascript
jQuery+pjax简单示例汇总
2017/04/21 jQuery
浅谈angular.js跨域post解决方案
2017/08/30 Javascript
初识 Vue.js 中的 *.Vue文件
2017/11/22 Javascript
javaScript产生随机数的用法小结
2018/04/21 Javascript
详解原生JS动态添加和删除类
2019/03/26 Javascript
vue 查看dist文件里的结构(多种方式)
2020/01/17 Javascript
python迭代器与生成器详解
2016/03/10 Python
python 第三方库的安装及pip的使用详解
2017/05/11 Python
python+selenium实现登录账户后自动点击的示例
2017/12/22 Python
python numpy 常用随机数的产生方法的实现
2019/08/21 Python
Python求两个字符串最长公共子序列代码实例
2020/03/05 Python
Canvas 文本填充线性渐变的使用详解
2020/06/22 HTML / CSS
面向对象设计的原则是什么
2013/02/13 面试题
领导干部考察材料
2014/02/08 职场文书
幼儿园小班教师寄语
2014/04/03 职场文书
2014年教师节寄语
2014/04/03 职场文书
伊索寓言教学反思
2014/05/01 职场文书
镇政府副镇长群众路线专题民主生活会对照检查材料
2014/09/19 职场文书
幼儿园安全管理制度
2015/08/05 职场文书
TV动画《神废柴☆偶像》公布先导PV
2022/03/20 日漫