VBA中的多线程

| 这里有人知道如何使VBA运行多个线程吗?我正在使用Excel。     
已邀请:
无法使用VBA本身完成。 VBA内置在单线程单元中。获取多个线程的唯一方法是在具有COM接口的VBA之外的其他版本中构建DLL,然后从VBA调用它。 INFO:描述和OLE线程模型的工作     
如您所知,VBA本身不支持多线程。有3种方法可以实现多线程: COM / dll-例如C#和Parallel类在单独的线程中运行 使用VBscript工作线程-在单独的VBscript线程中运行VBA代码 使用执行的VBA工作线程,例如通过VBscript-复制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的有趣概念。 基本上,您需要创建辅助vbscript来执行所需的各种操作,并将其报告回excel。对于我正在做的事情,从各种网站检索HTML数据,效果很好! 看一看: http://www.excelhero.com/blog/2010/05/multi-threaded-vba.html     
我要添加这个答案,是因为程序员从更现代的语言进入VBA,并且在Stack Overflow中搜索VBA中的多线程可能没有意识到几种原生VBA方法,这些方法有时有助于弥补VBA缺乏真正的多线程。 如果多线程的动机是使执行长时间运行的代码时不响应的UI不会挂起,则VBA确实有一些通常在实践中起作用的低技术解决方案: 1)可以使用户表单无模式显示-允许用户在表单打开时与Excel交互。这可以在运行时通过将Userform \的ShowModal属性设置为false来指定,也可以通过将行放置为从加载中动态完成
UserForm1.Show vbModeless
在用户窗体的initialize事件中。 2)DoEvents语句。这使VBA放弃对OS的控制权以执行事件队列中的任何事件-包括Excel生成的事件。典型的用例是在执行代码时更新图表。如果没有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
实现对参数的支持应该足够容易,但是很难返回值。     
如前所述,VBA不支持多线程。 但是您不需要使用C#或vbScript来启动其他VBA工作线程。 我使用VBA创建VBA工作线程。 首先,为您要启动的每个线程复制makro工作簿。 然后,您只需创建Excel.Application实例即可启动新的Excel实例(在另一个线程中运行)(为避免错误,我必须将新应用程序设置为可见)。 为了在另一个线程中实际运行某些任务,我可以使用主工作簿中的参数在另一个应用程序中启动一个makro。 要不等待就返回主工作簿线程,我只需在工作线程(我需要的地方)中使用Application.OnTime。 作为信号量,我只使用与所有线程共享的集合。 对于回调,将主工作簿传递给工作线程。在那里,可以重新使用runMakroInOtherInstance函数来启动回调。
\'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
    

要回复问题请先登录注册