Pythonの非同期処理の標準ライブラリであるasyncioを用いて非同期処理を実 行している時にOSからシグナルを受信してシグナルハンドラに登録した関数が 呼び出されるとPythonのGILの性質により実行されていた非同期処理は一時的 に処理を中断することを確認した。

サンプルコード

#! /usr/bin/env python
import asyncio
import itertools
import signal
import time


def signal_handler(signum, frame):
    for ii in range(10):
        print(f"SIGNAL: {signum} {ii}")
        time.sleep(1)


async def main():
    for ii in itertools.count():
        print(f"MAIN: {ii}")
        await asyncio.sleep(1)


signal.signal(signal.SIGTERM, signal_handler)
asyncio.run(main())

動作の解説

シグナルハンドラ

シグナルハンドラをSIGTERMで呼び出されるように登録した後メインループを実行する。 シグナルハンドラは1秒毎に f"SIGNAL: {signum} {ii}" とprintし10秒後に終了する。 signumはシグナルの番号、iiは0から始まり1ずつインクリメントする。

メインループ

メインループは1秒毎に f"MAIN: {ii}" とprintする。 iiは0から始まり1ずつインクリメントする。

シグナルの登録とシグナル受信時の処理

まずはメインループが実行される。その後SIGTERMを受信するとメインループ は停止しシグナルハンドラが実行される。シグナルハンドラが実行中はメイン ループの処理は実行されない。シグナルハンドラが完了するとメインループの 処理は再開される。

実行例

スクリプトを実行したときの出力を以下に示す。

MAIN: 0
MAIN: 1
MAIN: 2
MAIN: 3
MAIN: 4
MAIN: 5
MAIN: 6
MAIN: 7
MAIN: 8
MAIN: 9
MAIN: 10
MAIN: 11
MAIN: 12
MAIN: 13
MAIN: 14
MAIN: 15
SIGNAL: 15 0       ※ ここでシグナルを受信
SIGNAL: 15 1
SIGNAL: 15 2
SIGNAL: 15 3
SIGNAL: 15 4
SIGNAL: 15 5
SIGNAL: 15 6
SIGNAL: 15 7
SIGNAL: 15 8
SIGNAL: 15 9
MAIN: 16
MAIN: 17
MAIN: 18
MAIN: 19
MAIN: 20
MAIN: 21
MAIN: 22
MAIN: 23
MAIN: 24
MAIN: 25
MAIN: 26
MAIN: 27
MAIN: 28
MAIN: 29
MAIN: 30

結論

asyncioで非同期処理が行われている時にシグナルハンドラが呼び出されると シグナルハンドラの実行中はasyncioの非同期処理は一時的に停止する。