假设有这样一个需求,需要不断地从Redis读取数据,并将数据写入MongoDB。你可以这样写代码:1].decode())handler.insert_one(data)但是这样写有一个问题,就是每条数据都需要连接一次MongoDB,大量时间浪费在网络I/欧。所以大家将代码改成如下:=json.loads(data_raw[1].decode())to_be_insert.append(data)iflen(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的信号库。然后自定义一个信号回调函数,当Python收到信号时调用。所以让我们修改上面的代码::data_raw=client.blpop('data',timeout=300)ifnotdata_raw:continuedata=json.loads(data_raw[1].decode())to_be_insert.append(data)iflen(to_be_insert)>=1000:handler.insert_many(to_be_insert)to_be_insert=[]ifto_be_insert:handler.insert_many(to_be_insert)我们定义了一个全局变量stop,默认为False,所以默认情况下,whilenotstop所在的循环会继续运行。我们定义一个函数keyboard_handler,它的作用是修改全局变量stop为True。需要注意的是,要在函数中修改一个全局变量,首先要使用全局变量名将该变量声明为全局变量。否则无法修改。修改后,whilenotstop循环停止,于是程序进入:ifto_be_insert:handler.insert_many(to_be_insert)只要列表中有数据,就会批量插入到MongoDB中。然后程序结束。整个代码的关键是signal.signal(signal.SIGINT,keyboard_handler),其中信号SIGINT与函数keyboard_handler相关联。因此,在上述代码运行的任何时候,只要在键盘上按下Ctrl+C,程序就会进入keyboard_handler函数,并先执行这个函数中的代码。执行完成后,回到之前中断的地方,继续执行之前没有完成的代码。又因为我在函数中修改了stop的值,导致原来的循环无法继续执行,所以进入最后的收尾工作。需要注意的是,如果你的整个代码都是用Python编写的,那么信号可以在你程序的任何阶段被触发。只要按下Ctrl+C,就会立即进入设置信号回调函数。但是如果你的部分代码是用C语言写的,那么当你按下Ctrl+C的时候,你可能需要等待C语言的代码完成,才能进入你设置的信号回调函数。
