VBA中的multithreading
有没有人知道如何让VBA运行multithreading? 我正在使用Excel。
不能用VBA本地完成。 VBA是build在一个单线程的公寓。 获得多个线程的唯一方法是在VBA之外的某个具有COM接口的DLL中构build一个DLL,并从VBA调用它。
INFO:OLE线程模型的描述和工作原理
我正在寻找类似的东西,官方的答案是否定的。 但是,我在ExcelHero.com上能够findDaniel的一个有趣的概念。
基本上,你需要创build工人的脚本执行你想要的各种东西,并把它报告回Excel。 对于我正在做的,从各种网站检索HTML数据,它很好!
看一看:
http://www.excelhero.com/blog/2010/05/multi-threaded-vba.html
正如您可能了解的,VBA本身并不支持multithreading,但是。 有3种方法可以实现multithreading:
- COM / DLL – 例如C#和并行类在单独的线程中运行
- 使用VBscript工作线程 – 在单独的VBscript线程中运行您的VBA代码
- 使用例如通过VBScript执行的VBA工作线程 – 复制Excel工作簿并行运行macros。
我在这里比较了所有的线程方法: http : //analystcave.com/excel-multithreading-vba-vs-vbscript-vs-c-net/
考虑方法#3我也做了一个VBAmultithreading工具,允许您轻松地添加multithreading到VBA: http : //analystcave.com/excel-vba-multithreading-tool/
看下面的例子:
multithreadingFor循环
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
asynchronous运行Excelmacros
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
我join了这个答案,因为程序员从更现代的语言来到VBA,并在VBA中searchStack Overflow进行multithreading可能不知道有几种本地VBA方法,有时有助于补偿VBA缺乏真正的multithreading。
如果multithreading的动机是要有一个响应更快的用户界面(UI),而这个用户界面在长时间运行的代码执行时不会挂起,那么VBA确实有一些低技术含量的解决scheme,
1)可以使用户窗体无模式显示 – 这允许用户在窗体打开时与Excel进行交互。 这可以在运行时通过将Userform的ShowModal属性设置为false来指定,或者可以通过放置行
UserForm1.Show vbModeless
在用户窗体的初始化事件中。
2)DoEvents声明。 这导致VBA将控制权交给操作系统来执行事件队列中的任何事件 – 包括由Excel生成的事件。 典型的用例是在代码执行时更新图表。 没有DoEvents,图表将不会被重新绘制,直到macros运行之后,而Doevents可以创buildanimation图表。 这个想法的一个变种是创build进度计的常见技巧。 在执行10,000,000次(由循环索引i控制)的循环中,可以有一段代码:
If i Mod 10000 = 0 Then UpdateProgressBar(i) 'code to update progress bar display DoEvents End If
这些都不是multithreading – 但在某些情况下可能是一个足够的kludge。
我知道这个问题指定了Excel,但是由于Access的问题被标记为重复,所以我会在这里发表我的答案。 其原理很简单:打开一个新的Access应用程序,然后在该应用程序中打开一个带有计时器的表单,将要执行的函数/子发送到该表单,如果计时器命中,则执行该任务,并在执行完成后退出应用程序完了。 这使VBA能够处理来自数据库的表和查询。 注意:如果你专门locking了数据库,它会抛出错误。
这是所有的VBA(而不是其他答案)
asynchronous运行子/函数的函数
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
达到这个要求的forms的模块
(表单名称= 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
实现对参数的支持应该很容易,但是返回值很困难。