Program Club

VBA의 다중 스레딩

proclub 2021. 1. 7. 08:17
반응형

VBA의 다중 스레딩


여기 아무도 VBA가 여러 스레드를 실행하는 방법을 알고 있습니까? Excel을 사용하고 있습니다.


VBA로는 기본적으로 수행 할 수 없습니다. VBA는 단일 스레드 아파트에 구축되었습니다. 여러 스레드를 가져 오는 유일한 방법은 COM 인터페이스가있는 VBA 이외의 다른 항목에서 DLL을 빌드하고 VBA에서 호출하는 것입니다.

정보 : 설명 및 OLE 스레딩 모델 작업


아마 배웠 듯이 VBA는 기본적으로 멀티 스레딩을 지원하지 않지만. 멀티 스레딩을 달성하는 방법에는 세 가지가 있습니다.

  1. COM / dll- 예를 들어 C # 및 Parallel 클래스를 별도의 스레드에서 실행
  2. VBscript 작업자 스레드 사용 -별도의 VBscript 스레드에서 VBA 코드 실행
  3. 예를 들어 VBscript를 통해 실행되는 VBA 작업자 스레드 사용-Excel 통합 문서를 복사하고 매크로를 병렬로 실행합니다.

여기에서 모든 스레드 접근 방식을 비교했습니다. http://analystcave.com/excel-multithreading-vba-vs-vbscript-vs-c-net/

접근 방식 # 3을 고려하여 VBA에 멀티 스레딩을 쉽게 추가 할 수있는 VBA 멀티 스레딩 도구도 만들었습니다. http://analystcave.com/excel-vba-multithreading-tool/

아래 예를 참조하십시오.

For 루프 멀티 스레딩

Sub RunForVBA(workbookName As String, seqFrom As Long, seqTo As Long)
    For i = seqFrom To seqTo
        x = seqFrom / seqTo
    Next i
End Sub

Sub RunForVBAMultiThread()
    Dim parallelClass As Parallel 

    Set parallelClass = New Parallel 

    parallelClass.SetThreads 4 

    Call parallelClass.ParallelFor("RunForVBA", 1, 1000) 
End Sub

Excel 매크로를 비동기 적으로 실행

Sub RunAsyncVBA(workbookName As String, seqFrom As Long, seqTo As Long)
    For i = seqFrom To seqTo
        x = seqFrom / seqTo
    Next i
End Sub

Sub RunForVBAAndWait()
    Dim parallelClass As Parallel

    Set parallelClass  = New Parallel

    Call parallelClass.ParallelAsyncInvoke("RunAsyncVBA", ActiveWorkbook.Name, 1, 1000) 
    'Do other operations here
    '....

    parallelClass.AsyncThreadJoin 
End Sub

나는 비슷한 것을 찾고 있었고 공식적인 대답은 아니오입니다. 그러나 ExcelHero.com에서 Daniel의 흥미로운 개념을 찾을 수있었습니다.

기본적으로 원하는 다양한 작업을 실행하고 Excel로 다시보고하려면 작업자 vbscript를 만들어야합니다. 내가하는 일을 위해 다양한 웹 사이트에서 HTML 데이터를 검색하면 훌륭하게 작동합니다!

구경하다:

http://www.excelhero.com/blog/2010/05/multi-threaded-vba.html


프로그래머가 더 현대적인 언어에서 VBA로 이동하고 VBA에서 다중 스레딩을 위해 Stack Overflow를 검색하면 VBA의 진정한 다중 스레딩 부족을 보완하는 데 도움이되는 몇 가지 기본 VBA 접근 방식을 인식하지 못할 수 있기 때문에이 답변을 추가하고 있습니다.

멀티 스레딩의 동기가 장기 실행 코드가 실행될 때 중단되지 않는 응답 성이 더 높은 UI를 갖는 것이라면 VBA에는 실제로 작동하는 몇 가지 낮은 기술 솔루션이 있습니다.

1) 사용자 양식을 모덜리스로 표시하도록 만들 수 있습니다.이를 통해 사용자는 양식이 열려있는 동안 Excel과 상호 작용할 수 있습니다. 이것은 런타임에 Userform의 ShowModal 속성을 false로 설정하여 지정하거나 다음 줄을 입력하여로드에서 동적으로 수행 할 수 있습니다.

UserForm1.Show vbModeless

사용자 양식의 초기화 이벤트에서.

2) DoEvents 문. 이로 인해 VBA는 Excel에서 생성 된 이벤트를 포함하여 이벤트 큐의 모든 이벤트를 실행하도록 OS에 제어를 넘깁니다. 일반적인 사용 사례는 코드가 실행되는 동안 차트를 업데이트하는 것입니다. DoEvents가 없으면 매크로가 실행될 때까지 차트가 다시 그려지지 않지만 Doevents를 사용하면 애니메이션 차트를 만들 수 있습니다. 이 아이디어의 변형은 진행률 측정기를 만드는 일반적인 트릭입니다. 10,000,000 번 실행하는 루프 (루프 인덱스 i에 의해 제어 됨 )에서 다음과 같은 코드 섹션을 가질 수 있습니다.

If i Mod 10000 = 0 Then
    UpdateProgressBar(i) 'code to update progress bar display
    DoEvents
End If

이 중 어느 것도 멀티 스레딩이 아니지만 어떤 경우에는 적절한 클러지가 될 수 있습니다.


질문이 Excel을 지정한다는 것을 알고 있지만 Access에 대한 동일한 질문이 중복으로 표시되었으므로 여기에 답변을 게시하겠습니다. 원칙은 간단합니다. 새 Access 응용 프로그램을 연 다음 해당 응용 프로그램 내에서 타이머가있는 양식을 열고, 실행할 함수 / 서브를 해당 양식으로 보내고, 타이머에 도달하면 작업을 실행하고, 실행이 완료되면 응용 프로그램을 종료합니다. 끝마친. 이를 통해 VBA는 데이터베이스의 테이블 및 쿼리로 작업 할 수 있습니다. 참고 : 데이터베이스를 독점적으로 잠근 경우 오류가 발생합니다.

이것은 모든 VBA입니다 (다른 답변과 달리)

하위 / 함수를 비동기 적으로 실행하는 함수

Public Sub RunFunctionAsync(FunctionName As String)
    Dim A As Access.Application
    Set A = New Access.Application
    A.OpenCurrentDatabase Application.CurrentProject.FullName
    A.DoCmd.OpenForm "MultithreadingEngine"
    With A.Forms("MultiThreadingEngine")
        .TimerInterval = 10
        .AddToTaskCollection (FunctionName)
    End With
End Sub

이를 달성하는 데 필요한 양식의 모듈

(양식 이름 = MultiThreadingEngine, 컨트롤이나 속성이 설정되지 않음)

Public TaskCollection As Collection

Public Sub AddToTaskCollection(str As String)
    If TaskCollection Is Nothing Then
        Set TaskCollection = New Collection
    End If
    TaskCollection.Add str
End Sub
Private Sub Form_Timer()
    If Not TaskCollection Is Nothing Then
        If TaskCollection.Count <> 0 Then
            Dim CollectionItem As Variant
            For Each CollectionItem In TaskCollection
                Run CollectionItem
            Next CollectionItem
        End If
    End If
    Application.Quit
End Sub

매개 변수에 대한 지원을 구현하는 것은 충분히 쉬워야하지만 값을 반환하는 것은 어렵습니다.


As said before, VBA does not support Multithreading.

But you don't need to use C# or vbScript to start other VBA worker threads.

I use VBA to create VBA worker threads.

First copy the makro workbook for every thread you want to start.

Then you can start new Excel Instances (running in another Thread) simply by creating an instance of Excel.Application (to avoid errors i have to set the new application to visible).

To actually run some task in another thread i can then start a makro in the other application with parameters form the master workbook.

To return to the master workbook thread without waiting i simply use Application.OnTime in the worker thread (where i need it).

As semaphore i simply use a collection that is shared with all threads. For callbacks pass the master workbook to the worker thread. There the runMakroInOtherInstance Function can be reused to start a callback.

'Create new thread and return reference to workbook of worker thread
Public Function openNewInstance(ByVal fileName As String, Optional ByVal openVisible As Boolean = True) As Workbook
    Dim newApp As New Excel.Application
    ThisWorkbook.SaveCopyAs ThisWorkbook.Path & "\" & fileName
    If openVisible Then newApp.Visible = True
    Set openNewInstance = newApp.Workbooks.Open(ThisWorkbook.Path & "\" & fileName, False, False) 
End Function

'Start macro in other instance and wait for return (OnTime used in target macro)
Public Sub runMakroInOtherInstance(ByRef otherWkb As Workbook, ByVal strMakro As String, ParamArray var() As Variant)
    Dim makroName As String
    makroName = "'" & otherWkb.Name & "'!" & strMakro
    Select Case UBound(var)
        Case -1:
            otherWkb.Application.Run makroName
        Case 0:
            otherWkb.Application.Run makroName, var(0)
        Case 1:
            otherWkb.Application.Run makroName, var(0), var(1)
        Case 2:
            otherWkb.Application.Run makroName, var(0), var(1), var(2)
        Case 3:
            otherWkb.Application.Run makroName, var(0), var(1), var(2), var(3)
        Case 4:
            otherWkb.Application.Run makroName, var(0), var(1), var(2), var(3), var(4)
        Case 5:
            otherWkb.Application.Run makroName, var(0), var(1), var(2), var(3), var(4), var(5)
    End Select
End Sub

Public Sub SYNCH_OR_WAIT()
    On Error Resume Next
    While masterBlocked.Count > 0
        DoEvents
    Wend
    masterBlocked.Add "BLOCKED", ThisWorkbook.FullName
End Sub

Public Sub SYNCH_RELEASE()
    On Error Resume Next
    masterBlocked.Remove ThisWorkbook.FullName
End Sub

Sub runTaskParallel()
    ...
    Dim controllerWkb As Workbook
    Set controllerWkb = openNewInstance("controller.xlsm")

    runMakroInOtherInstance controllerWkb, "CONTROLLER_LIST_FILES", ThisWorkbook, rootFold, masterBlocked
    ...
End Sub

Sub MultiProcessing_Principle()
    Dim k As Long, j As Long
    k = Environ("NUMBER_OF_PROCESSORS")
    For j = 1 To k
        Shellm "msaccess", "C:\Autoexec.mdb"
    Next
    DoCmd.Quit
End Sub

Private Sub Shellm(a As String, b As String) ' Shell modificirani
    Const sn As String = """"
    Const r As String = """ """
    Shell sn & a & r & b & sn, vbMinimizedNoFocus
End Sub

ReferenceURL : https://stackoverflow.com/questions/5721564/multi-threading-in-vba

반응형