【入門編】【自作タイマーによるブロック処理回避】WScript.Sleepによる全体停止を避け特定処理のみを定常間隔で実行するタイムスケジュール制御 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!VBScriptの世界へようこそ。
マクロの記録や、上から順番に実行されるだけの単純なスクリプトに少し物足りなさを感じていませんか?「ここをクリアすれば、VBScriptの基本はバッチリですよ!」と自信を持って言える、ちょっと誇らしくなるような高度なテクニックを今回は一緒に学んでいきましょう。

エンジニアとして現場に出ると、避けて通れないのが「タイミングの制御」です。
今回は、VBScriptの弱点を鮮やかに克服し、複数の処理をまるで同時に動いているかのようにさばく「イベントループによるタイムスケジュール制御」の極意を伝授します。

—

1. なぜ `WScript.Sleep` は諸刃の剣なのか?

VBScriptで「ちょっと処理を止めて待機したい」と思ったとき、誰もが最初に使うのが `WScript.Sleep 1000`(1秒停止)です。

‘ 【やってはいけない典型例】
WScript.Echo “処理A開始”
WScript.Sleep 5000 ‘ 5秒間、スクリプト全体の時間が完全に止まる!
WScript.Echo “処理B開始”

この `WScript.Sleep` は非常に分かりやすい反面、「スレッドの完全停止(ブロック)」を引き起こします。
もしあなたが「1秒ごとにログを監視しつつ、10秒ごとにデータをバックアップしたい」と思ったとき、`WScript.Sleep` を使ってしまうと、他のすべての処理がフリーズしてしまいます。これでは、実用的な自動化ツールは作れません。

解決策:時間を「止める」のではなく、時間を「見る」

プロの現場では、スクリプトを眠らせる(Sleep)のではなく、「現在時刻を監視し続け、その時が来たら実行する」というアプローチをとります。これが今回解説するイベントループ構造です。

—

2. 実装の全体像:非同期風タイムスケジュール制御

まずは、百聞は一見に如かず。コピペしてすぐにWindows環境(ダブルクリック)で動かせる完全なコードをお見せします。

このコードは、以下の2つの異なるタスクを同時に(非同期風に)実行します。
1. タスクA:2秒に1回実行される処理
2. タスクB:5秒に1回実行される処理
(※終了するには、コマンドプロンプト上で `Ctrl + C` を押してください)

‘ ==============================================================================
‘ スクリプト名: AsyncTimerSample.vbs
‘ 概要: WScript.Sleepを使わず、Timer関数でイベントループを構築するサンプル
‘ ==============================================================================

Option Explicit

Sub Main
WScript.Echo “=== タイムスケジュール制御を開始します (終了: Ctrl+C) ===”

‘ 各タスクの「前回の実行時刻(タイマー)」を保持する変数
Dim nextTaskA, nextTaskB

‘ 実行間隔(秒単位)の設定
Const INTERVAL_A = 2.0
Const INTERVAL_B = 5.0

‘ Timer関数は「真夜中からの経過秒数(浮動小数点)」を返します
Dim currentTime
currentTime = Timer()

‘ 初回実行のタイミングを現在時刻に設定
nextTaskA = currentTime
nextTaskB = currentTime

‘ — 【これがイベントループの心臓部です】 —
Do
‘ 現在の時刻を常に取得し続ける
currentTime = Timer()

‘ — タスクAのスケジュール判定 —
If currentTime >= nextTaskA Then
Call DoTaskA()
‘ 次回実行時間を「現在時刻 + インターバル」に再設定
nextTaskA = currentTime + INTERVAL_A
End If

‘ — タスクBのスケジュール判定 —
If currentTime >= nextTaskB Then
Call DoTaskB()
‘ 次回実行時間を設定
nextTaskB = currentTime + INTERVAL_B
End If

‘ CPUの稼働率100%を防ぐため、ごく短い休息を入れる(※ここでのSleepは全体停止ではなく息継ぎ)
‘ ※極限まで精度を上げる場合は省略も可能ですが、CPU負荷に注意してください
WScript.Sleep 10

Loop
End Sub

‘ ——————————————————————————
‘ タスクA(2秒に1回動く処理のつもり)
‘ ——————————————————————————
Sub DoTaskA()
WScript.Echo “[” & FormatDateTime(Now, 4) & “] 【タスクA】 2秒ごとの定常チェック実行”
End Sub

‘ ——————————————————————————
‘ タスクB(5秒に1回動く処理のつもり)
‘ ——————————————————————————
Sub DoTaskB()
WScript.Echo “[” & FormatDateTime(Now, 4) & “]   ==> 【タスクB】 5秒ごとのデータ送信バッファ確認”
End Sub

‘ メイン処理の呼び出し
Main

—

3. コードの急所:なぜこの構造でうまくいくのか?

初心者の方に向けて、このコードの重要なポイントを3つに絞って解説します。

① `Timer()` 関数の魔力

VBScriptの `Timer()` 関数は、その日の深夜0時からの経過秒数を浮動小数点(ミリ秒単位の精度)で返します。
「今の時間は何秒か?」を常にループ内で監視し、設定した未来の時刻(`nextTaskA` など)を超えた瞬間をキャッチして処理を走らせます。

② 無限ループ(`Do … Loop`)とCPU負荷のバランス

イベントループは常に回り続けます。もし何も考えずに `Do` ループを回すと、PCのCPU使用率が片側100%に張り付いてファンが唸りを上げます。
それを防ぐために最後に置いているのが `WScript.Sleep 10` です。
「たった10ミリ秒(0.01秒)だけCPUに休暇を与える」ことで、システム全体の動作を滑らかに保ちつつ、人間の感覚ではリアルタイムに動く処理を実現できます。

③ 日付をまたぐ瞬間の考慮(応用へのステップ)

`Timer()` 関数は日付が変わると `0` にリセットされます。厳密に24時間以上動き続ける常駐システムを作る場合は、このリセットの瞬間に `nextTaskA` がバグる可能性(時刻の逆転現象)を考慮する必要があります。
今回はシンプルに数分〜数時間の自動化タスクを想定していますが、プロとして意識しておくだけで視座がグッと上がります。

—

4. 現場でよくある「落とし穴」とエラー対策

このイベントループ構造を実務のコードに組み込む際、初心者が陥りがちな罠がいくつかあります。

  • 罠1:タスク内の処理が重すぎてループが止まる
  • `DoTaskA` の中で、もし「10秒かかる重いファイルコピー」などを同期実行してしまうと、その間イベントループ自体が止まってしまい、タスクBの実行タイミングが大幅に遅れます。
  • 対策: ループ内で呼び出す関数は「一瞬で終わる処理」にするか、重い処理が必要な場合は別の非同期オブジェクト(WScript.Shellの `Run` メソッドの非同期実行など)を呼び出す設計にしましょう。
  • 罠2:エラーハンドリング(`On Error Resume Next`)の呪縛
  • 常駐的なイベントループを書く際、予期せぬエラー(ファイルのロックなど)でスクリプト全体が強制終了しては困ります。
  • 対策: ループの内部、あるいは各タスクの内部で適切にエラーをキャッチし、ログを残してループを継続させるタフな設計(レジリエンス)を心がけましょう。

—

まとめ:ここをクリアすれば、あなたはもう初心者ではない!

今回は、`WScript.Sleep` の呪縛を解き放ち、`Timer` 関数を用いたイベントループによるタイムスケジュール制御を解説しました。

  • スクリプト全体を眠らせるのではなく、時刻を監視して「その時が来たら動く」
  • 複数の異なる時間軸の処理を、1つのループで同時にさばく

この概念を理解できれば、単なる「上から下へ流れるマクロ」から、「意志を持ったスマートな自動化ツール」へとステップアップできます。

ここをクリアしたあなたなら、VBScriptの基礎はもうバッチリです。ぜひ実際の業務ツールに応用してみてくださいね。それでは、次のレベルでお会いしましょう!

タイトルとURLをコピーしました