0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ExcelVBAで疑似的なマルチスレッドを実現する

Posted at

環境

  • Excel for Microsoft365(64bit)

経緯

ExcelVBAで以下のような機能を要求されました。

  • ボタン1を押すと監視処理Aを開始する
  • 監視処理Aは50ms周期で状態の監視と、その結果をVBAフォーム上に表示する
  • ボタン2を押すと処理Bを開始する
  • 処理Bは1秒ごとに内部のパラメータを変更し、これを10回繰り返す

当初は監視処理Aのみ要求されていたので、ボタンイベントの中で無限ループを行う形で処理を実現していました。
その後、処理Bを追加することになりましたが、同様にボタンイベントで実装したところ、処理Bが実行されている間、監視処理Aが動かなくなりました。
これはExcelVBAがシングルスレッドで動作するためであり、二つの処理を同時に実行できないことが原因でした。

この問題を解決するため、次にあげる方法で疑似的なマルチスレッド処理を実現しました。

Application.OnTimeによる疑似マルチスレッド

Application.OnTimeは指定した時刻にプロシージャ(マクロ)を実行します。
指定した時刻、今回であれば処理Bを1秒ごとに呼び出すことで、監視処理Aが実行できるようにします。

UserForm
Private LoopActive As Boolean
Private CounterB As Integer
Private ReservedTimeB As Date

Private Sub 監視処理A_Click()
    If 監視処理A.Value = True Then
        '開始
        LoopActive = True
        Application.OnTime EarliestTime:=Now, Procedure:="FunctionA"
    Else
        '停止
        LoopActive = False
    End If
End Sub

Private Sub 処理B_Click()
    If 処理B.Value = True Then
        '開始
        CounterB = 0
        '処理Bの実行時間を保持(停止時に必要なため)
        ReservedTimeB = Now + TimeValue("00:00:01")
        Application.OnTime EarliestTime:=ReservedTimeB, Procedure:="FunctionB"
    Else
        If CounterB < 10 Then
            '停止(次回予約されている時のみ
            Application.OnTime EarliestTime:=ReservedTimeB, Procedure:="FunctionB", Schedule:=False
        End If
    End If
End Sub

Public Sub FunctionA_Loop()
    While (LoopActive)
        '==========
        '監視処理A
        '==========
        
        'その他のイベント、タイマー処理に実行権を移す
        DoEvents
    Wend
End Sub

Public Sub FunctionB_Loop()
    '==========
    '処理B
    '==========
    CounterB = CounterB + 1
    
    '次回起動予約(1秒後)
    If CounterB < 10 Then
        Application.OnTime EarliestTime:=ReservedTimeB, Procedure:="FunctionB"
    End If
End Sub
標準モジュール
'OnTimeで呼び出される処理を定義
Public Sub FunctionA()
    UserForm1.FunctionA_Loop
End Sub

Public Sub FunctionB()
    UserForm1.FunctionB_Loop
End Sub

調べてみると、VBScriptやExcelを別プロセスで起動する方法もあるようですが、デバッグができなかったり、制御が難しかったりします。(バックグラウンドにExcelのプロセスが残ったり)
そういった点からApplication.OnTimeを使う方法がベストかと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?