はじめに
Python で定期処理を実行する方法として、以下の schedule モジュールを紹介しました。
schedule モジュールを使用した場合、一般的にイメージする定期処理を行うことができず、
実行周期のズレが発生していました。
今回は、そのズレを起こさない「定周期処理」の実現方法として、
シグナル(Signal)を使う方法をサンプルを交えながら紹介します。
期待する定周期処理
この記事では、以下の動作を期待する「定周期処理」として解説しています。
例えば、5秒間隔(t=5秒)に1回実行したい処理がある場合、Job が完了する時間に左右されることなく、
定期的に Job が実行されることを期待値として考えます。
シグナル(signal)の基本的な使い方
使い方
Unix 系 OS では、時間の経過を知らせるシグナルとして SIGALRM が用意されています。
Python で SIGALRM を使用する場合は、以下のように記述します。
1 2 3 4 5 6 7 8 |
import signal def handler(signum, frame): print('Signal handler called with signal', signum) signal.signal(signal.SIGALRM, handler) signal.setitimer(signal.ITIMER_REAL, 5, 5) |
書式
1 2 3 |
signal.signal(signal.SIGALRM, <呼び出したい関数>) signal.setitimer(signal.ITIMER_REAL, 1回目の実行までの時間, 2回目以降の実行間隔) |
シグナル(signal)を使用する場合の注意点
シグナルは Unix 系システムにおける特殊な仕組みで、
ソフトウェアから見ると「割り込み」のような動作をします。
そのため、登録したハンドラーの中で行えることに制限が生まれます。
これは Python 言語に限った話ではなく、他の言語においても注意しなければなりません。
C言語のページで詳しく解説していますので、こちらも参考にどうぞ。
一方、Python はインタプリタ言語です。
Python のコードを構文解析して実行するエンジンは、C言語で記述されています。
SIGALRM 周辺のコードは、以下のように記述されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
PyOS_sighandler_t PyOS_setsig(int sig, PyOS_sighandler_t handler) { #ifdef HAVE_SIGACTION (snip) if (sigaction(sig, &context, &ocontext) == -1) return SIG_ERR; return ocontext.sa_handler; #else PyOS_sighandler_t oldhandler; oldhandler = signal(sig, handler); return oldhandler; #endif } static PyObject * signal_signal_impl(PyObject *module, int signalnum, PyObject *handler) { (snip) if (PyOS_setsig(signalnum, func) == SIG_ERR) { PyErr_SetFromErrno(PyExc_OSError); } (snip) } static PyObject * signal_setitimer_impl(PyObject *module, int which, PyObject *seconds, PyObject *interval) { _signal_module_state *modstate = get_signal_state(module); struct itimerval new; (snip) struct itimerval old; if (setitimer(which, &new, &old) != 0) { PyErr_SetFromErrno(modstate->itimer_error); return NULL; } return itimer_retval(&old); } |
ご覧の通り、sigaction や setitimer など、C言語で記述する場合と同じように
低レイヤーの関数を呼び出してハンドラーを登録しています。
つまり、Python でシグナルを扱う場合も同じ制約を受けるため、
ハンドラーの中で記述できる処理に制限があることを意味しています。
しかし、Python で呼び出したモジュールの処理を一つ一つ確認することは困難なため、
実際のところはシグナルを使えるケースが無いと言っていいでしょう。