专栏首页未闻Code一日一技:在 Python 中接管键盘中断信号

一日一技:在 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 语言的代码运行完成以后,才会进入你设置的信号回调函数中。

本文分享自微信公众号 - 未闻Code(itskingname),作者:kingname

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一日一技:引用计数什么时候+1什么时候-1

    引用计数是Python进行垃圾回收的主要技术之一。当一个对象的引用计数归零的时候,它就会被Python的垃圾回收清理掉。

    青南
  • 一日一技:用Python反重力

    青南
  • 一日一技:不用游标卡尺,Yaml 格式5分钟入门

    业界常常流传一个笑话,写 YAML 配置的时候,需要用游标卡尺比着屏幕来写。稍稍多一个空格少一个空格,配置文件就会报错。

    青南
  • 目标检测算法之YOLOv3及YOLOv3-Tiny

    昨天稍微填上了YOLOv2损失函数的坑,然后我在知乎关注了一个有趣的问题,地址是:https://www.zhihu.com/question/35700517...

    BBuf
  • 风靡全国,日活8000万,《王者荣耀》后台技术架构演进!

    这个曾经在端游时代主导搭建 RTS 游戏《霸三国》框架的技术团队,在转型做 MOBA 手游《王者荣耀》后为游戏提供了巨大的支持,但这个过程也并非一帆风顺。

    51CTO技术栈
  • 谈一谈大型网站架构的演进之路(二)

    从上一篇中,我们讲到了单体应用系统演变成中型网站项目的蜕变过程,今天就来聊一下他的架构模式和关键要素。

    23号杂货铺
  • 玩转CSS3动画

    因公司业务需要在Android WebView上增加对CSS3动画的支持,所以就先研究了一下CSS Animations。这篇文章主要站在前端开发人员的角度,试...

    云水木石
  • 计算机网络(二)网络层

    如果所请求对象在缓存中,缓存返回对象 如果不在缓存中,缓存服务器向原始服务器发送HTTP请求,获取对象,然后返回给客户端并保存

    星辉
  • 投资钛值的你,知道钛链是什么吗?

    技术 钛链技术到底有多牛?钛值从技术考量到底有多值?为普及和推广钛值背后的区块链技术,我们的钛值极客团队来给大家上课啦! 搬好小板凳,拿出笔记本,开始学习! 1...

    企鹅号小编
  • 【FAQ】数据科学是什么?

    小编邀请您,先思考: 1 什么是数据科学? Data science is a multidisciplinary blend of data inferenc...

    陆勤_数据人网

扫码关注云+社区

领取腾讯云代金券