忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

サービスプログラムの作り方について・その4(メイン処理内で別のスレッドを呼出す)

前回「その3」では「メイン処理スレッドの実際の中身を組み込んでいきたいと思います」と記しましたが、実際のプログラム例を示します。

今回の例ではメイン処理スレッド内で、時刻が毎分00秒になった場合、サブ処理のスレッドを起動します。
毎分00秒の判定方法は、メインループ内で毎回、現在時刻の秒を取得し、初期状態から"00"になった時にスレッド開始のトリガとしています。
スレッド開始時の秒を前回値として退避しておきます。 この前回値が設定済みで、現在時刻の秒が"00"ではない場合に、再度前回値を初期状態に戻しています。
この一連の処理はループ最後の待ち時間が1秒より十分小さくないと成り立ちません。
(尚、このトリガ時刻判定はもっといい方法があるかもしれませんが。)

サブ処理のスレッドの中身は、単に1秒毎に現在時刻をイベントログの書き出す処理を3回行っています。 3回の処理を終わるとこのスレッドは処理を終え、消滅します。
実際のソースは以下の通りです。

■サービスPG・メイン処理内で別のスレッドを呼出す

Public Class TestService

    Protected Overrides Sub OnStart(ByVal args() As String)
        ' サービスを開始するコードをここに追加します。このメソッドによって、
        ' サービスが正しく実行されるようになります
        Me.EventLog.WriteEntry("サービスを開始します。")

        'メイン処理スレッドの生成を行う
        Me.mThreadMain = New Threading.Thread(New Threading.ThreadStart(AddressOf Me.MainProc))
        Me.mThreadMain.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。
        Me.EventLog.WriteEntry("サービスを停止します。")

        'メインスレッド停止依頼フラグON
        Me.mblnThreadStop = True
        'メインスレッドの停止を待つ
        Me.mThreadMain.Join()
    End Sub

    ' スレッドクラス
    Private mThreadMain As Threading.Thread
    ' スレッド停止フラグ
    Private mblnThreadStop As Boolean = False

    ' メイン処理スレッド
    Private Sub MainProc()
        Me.EventLog.WriteEntry("MainProc...開始。")
        Try
            '前回秒の初期状態
            Dim strLastSS As String = ""
            'メインスレッドのループ
            While mblnThreadStop = False
                '現在の秒の取得
                Dim strSS As String = Now.ToString("ss")
                If strLastSS = "" Then
                    '前回秒の初期状態の場合
                    If strSS = "00" Then
                        '時刻が毎分00秒の場合、サブスレッド開始
                        Dim t As New Threading.Thread(New Threading.ThreadStart(AddressOf SubProc))
                        t.Start()
                        '現在の秒の退避
                        strLastSS = strSS
                    End If
                Else
                    If strSS <> "00" Then
                        '前回秒の初期化
                        strLastSS = ""
                    End If
                End If

                ' 約0.1秒待ち時間を入れる(このプロセスがCPUを独占しない様に)
                Threading.Thread.Sleep(100)
            End While

        Catch ex As Exception
            ' エラー処理があればここに書く
            Me.EventLog.WriteEntry("MainProc...エラー" & ex.Message)
        End Try
        Me.EventLog.WriteEntry("MainProc...停止。")
    End Sub

    'サブ処理スレッド
    Private Sub SubProc()
        Me.EventLog.WriteEntry("SubProc...開始。")
        Try
            'ループを3回で処理終了
            For i As Integer = 1 To 3
                '時刻の時間.分.秒.ミリ秒の表示
                Me.EventLog.WriteEntry("SubProc..." & Now.ToString("HH:mm:ss.fff"))
                ' 約1秒待ち時間を入れる
                Threading.Thread.Sleep(1000)
            Next

        Catch ex As Exception
            ' エラー処理があればここに書く
            Me.EventLog.WriteEntry("SubProc...エラー" & ex.Message)
        End Try
        Me.EventLog.WriteEntry("SubProc...停止。")
    End Sub

End Class

このサービスを実際に開始し、ログの結果を「その3」同様 Windowsの powershell を起動し Get-EventLog コマンド でログの一覧を見てみます。

「Windows + R」 で「ファイル名を指定して実行」を開き、 powershell を起動します。
powershell 起動後、以下のコマンドをキー入力してログ一覧を行います。

  • Get-EventLog Application -Newest 26

ログを見ますと、ほぼ毎分00秒でサブスレッドが起動され、1秒毎に3回時刻が登録されています。この例ではサービスを起動後、約4分後にサービスを停止させています。

サービスの処理では外部からのリクエストにより何かの処理を行い結果を返すというのが一般的な処理ではないかと思われます。 今回の例ではタイマをリクエストのトリガとして使っています。タイマもサービスプログラムから見れば外部と考えられなくもありませんが。
外部からのリクエストとしては、通信による方法だとか、原始的な方法ではファイルによるものだとかいろいろ考えられます。 今後は外部からのリクエストに応答するサービスを例に挙げていきたいと思います。

さて、今回の例でメイン処理のなかで、サブスレッドを起動していますが、 外部から複数の起動が掛かることを将来的に考えるのであれば、 サブスレッドを複数起動しそれぞれのスレッドのログが判別できる様にしてみます。

サブスレッドの処理をクラスでラップし、クラスの中に内部のスレッドの起動メソッドを設けてみます。

■サービスPG・メイン処理内で別のスレッドを呼出す(別スレッドをクラス化)

Public Class TestService

    Protected Overrides Sub OnStart(ByVal args() As String)
        ' サービスを開始するコードをここに追加します。このメソッドによって、
        ' サービスが正しく実行されるようになります
        Me.EventLog.WriteEntry("サービスを開始します。")

        'メイン処理スレッドの生成を行う
        Me.mThreadMain = New Threading.Thread(New Threading.ThreadStart(AddressOf Me.MainProc))
        Me.mThreadMain.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。
        Me.EventLog.WriteEntry("サービスを停止します。")

        'メインスレッド停止依頼フラグON
        Me.mblnThreadStop = True
        'メインスレッドの停止を待つ
        Me.mThreadMain.Join()
    End Sub

    ' スレッドクラス
    Private mThreadMain As Threading.Thread
    ' スレッド停止フラグ
    Private mblnThreadStop As Boolean = False

    ' メイン処理スレッド
    Private Sub MainProc()
        Me.EventLog.WriteEntry("MainProc...開始。")
        Try
            '前回秒の初期状態
            Dim strLastSS As String = ""
            'メインスレッドのループ
            While mblnThreadStop = False
                '現在の秒の取得
                Dim strSS As String = Now.ToString("ss")
                If strLastSS = "" Then
                    '前回秒の初期状態の場合
                    If strSS = "00" Then
                        '時刻が毎分00秒の場合、3個のサブスレッド開始
                        For i As Integer = 1 To 3
                            'スレッド名前
                            Dim strName As String = "Test" & i.ToString
                            'スレッドクラス生成
                            Dim clsSubTread As New SubThread(strName, Me.EventLog)
                            'クラス内のスレッド開始
                            clsSubTread.Start()
                        Next
                        '現在の秒の退避
                        strLastSS = strSS
                    End If
                Else
                    If strSS <> "00" Then
                        '前回秒の初期化
                        strLastSS = ""
                    End If
                End If

                ' 約0.1秒待ち時間を入れる(このプロセスがCPUを独占しない様に)
                Threading.Thread.Sleep(100)
            End While

        Catch ex As Exception
            ' エラー処理があればここに書く
            Me.EventLog.WriteEntry("MainProc...エラー" & ex.Message)
        End Try
        Me.EventLog.WriteEntry("MainProc...停止。")
    End Sub

    '=====
    'サブスレッド用ラップクラス
    '=====
    Class SubThread
        '名前
        Private mstrName As String
        'イベントログ
        Private mEventLog As System.Diagnostics.EventLog

        'コンストラクタ
        Sub New(strName As String, EventLog As System.Diagnostics.EventLog)
            Me.mstrName = strName
            Me.mEventLog = EventLog
        End Sub

        '内部スレッドの開始
        Sub Start()
            Dim t As New Threading.Thread(New Threading.ThreadStart(AddressOf SubProc))
            t.Start()
        End Sub

        'サブ処理スレッド
        Private Sub SubProc()
            Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...開始。")
            Try
                'ループを3回で処理終了
                For i As Integer = 1 To 3
                    '時刻の時間.分.秒.ミリ秒の表示
                    Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]..." & Now.ToString("HH:mm:ss.fff"))
                    ' 約1秒待ち時間を入れる
                    Threading.Thread.Sleep(1000)
                Next

            Catch ex As Exception
                ' エラー処理があればここに書く
                Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...エラー" & ex.Message)
            End Try
            Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...停止。")
        End Sub
    End Class

End Class

ログを見ますと、ほぼ毎分00秒で3個のサブスレッドが起動され、1秒毎に3回時刻が登録されています。
3個のスレッドは実行が順番通りではありません。これはスレッドを同時に起動したとしても、スレッドの優先がどれになるかは分からない様です。


サブスレッドをクラス化しましたが、各スレッドで少し違った処理をさせたい場合にはクラス化してその中で場合分けした方が組みやすいと思います。

関連する記事

サービスプログラムの作り方について・その1
サービスプログラムの作り方について・その2
サービスプログラムの作り方について・その3(メイン処理をスレッド化)

おすすめ書籍












PR

コメント

コメントを書く