multithreading - Multi-threading en VBA
excel excel-vba (7)
Como probablemente hayas aprendido, VBA no es compatible nativamente con multihilo, pero Hay 3 métodos para lograr multihilo:
- COM / dlls - por ejemplo C # y la clase Paralelo para ejecutar en hilos separados
- Uso de subprocesos de trabajo de VBscript : ejecute el código de VBA en subprocesos de VBscript separados
- Usando hilos de trabajo de VBA ejecutados, por ejemplo, a través de VBscript , copie el libro de Excel y ejecútelo en paralelo.
Comparé todos los enfoques de subprocesos aquí: http://analystcave.com/excel-multithreading-vba-vs-vbscript-vs-c-net/
Teniendo en cuenta el enfoque n. ° 3, también creé una herramienta VBA Multithreading que le permite agregar fácilmente subprocesamiento múltiple a VBA: http://analystcave.com/excel-vba-multithreading-tool/
Vea los ejemplos a continuación:
Multithreading a For Loop
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
Ejecutar una macro de Excel de forma asíncrona
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
¿Alguien aquí sabe cómo hacer que VBA ejecute varios hilos? Estoy usando Excel.
Como se dijo antes, VBA no es compatible con Multithreading.
Pero no necesita usar C # o vbScript para iniciar otros hilos de trabajo de VBA.
Uso VBA para crear hilos de trabajo de VBA .
Primero copie el libro de trabajo makro para cada tema que quiera iniciar.
Luego puede iniciar nuevas instancias de Excel (ejecutándose en otro subproceso) simplemente creando una instancia de Excel.Application (para evitar errores, tengo que configurar la nueva aplicación para que sea visible).
Para ejecutar realmente alguna tarea en otro hilo, puedo iniciar un makro en la otra aplicación con los parámetros del libro maestro.
Para volver al hilo del libro maestro sin esperar, simplemente uso Application.OnTime en el hilo del trabajador (donde lo necesito).
Como semáforo simplemente uso una colección que se comparte con todos los hilos. Para las devoluciones de llamada, pase el libro maestro al hilo de trabajo. Allí la función runMakroInOtherInstance puede reutilizarse para iniciar una devolución de llamada.
''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
Estaba buscando algo similar y la respuesta oficial es no. Sin embargo, pude encontrar un concepto interesante de Daniel en ExcelHero.com.
Básicamente, necesita crear vbscripts de trabajo para ejecutar las diversas cosas que desea y hacer que informe a excel. Por lo que estoy haciendo, recuperando datos HTML de varios sitios web, ¡funciona genial!
Echar un vistazo:
http://www.excelhero.com/blog/2010/05/multi-threaded-vba.html
Estoy agregando esta respuesta ya que los programadores que vienen a VBA desde idiomas más modernos y buscan para multithreading en VBA podrían desconocer un par de enfoques nativos de VBA que a veces ayudan a compensar la falta de multithreading verdadero de VBA.
Si la motivación del multihilo es tener una interfaz de usuario más receptiva que no se cuelga cuando se ejecuta el código de ejecución prolongada, VBA sí tiene un par de soluciones de baja tecnología que a menudo funcionan en la práctica:
1) Se puede hacer que las formas de usuario se muestren sin moderación, lo que permite al usuario interactuar con Excel mientras el formulario está abierto. Esto puede especificarse en tiempo de ejecución configurando la propiedad ShowModal del Userform en false o puede hacerse de forma dinámica a medida que la carga se realiza colocando la línea
UserForm1.Show vbModeless
en el evento de inicialización del formulario de usuario.
2) La declaración DoEvents. Esto hace que VBA ceda el control al sistema operativo para ejecutar cualquier evento en la cola de eventos, incluidos los eventos generados por Excel. Un caso de uso típico es actualizar un gráfico mientras el código se está ejecutando. Sin DoEvents, el gráfico no se volverá a pintar hasta que se ejecute la macro, pero con Doevents puede crear gráficos animados. Una variación de esta idea es el truco común de crear un medidor de progreso. En un bucle que se ejecutará 10,000,000 de veces (y controlado por el índice de bucle i ) puede tener una sección de código como:
If i Mod 10000 = 0 Then
UpdateProgressBar(i) ''code to update progress bar display
DoEvents
End If
Nada de esto es multihilo, pero podría ser un error adecuado en algunos casos.
Sé que la pregunta especifica Excel, pero dado que la misma pregunta para Access se marcó como duplicada, entonces publicaré mi respuesta aquí. El principio es simple: abra una nueva aplicación de Access, luego abra un formulario con un temporizador dentro de esa aplicación, envíe la función / sub que desea ejecutar a ese formulario, ejecute la tarea si el temporizador acierta y salga de la aplicación una vez que la ejecución terminado. Esto permite que VBA trabaje con tablas y consultas desde su base de datos. Nota: arrojará errores si ha bloqueado exclusivamente la base de datos.
Esto es todo VBA (a diferencia de otras respuestas)
La función que ejecuta un sub / función de forma asíncrona
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
El módulo del formulario requerido para lograr esto
(nombre del formulario = MultiThreadingEngine, no tiene controles ni propiedades establecidas)
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
La implementación del soporte para parámetros debería ser lo suficientemente fácil, sin embargo, la devolución de valores es difícil.
No se puede hacer de forma nativa con VBA. VBA está construido en un apartamento de un solo hilo. La única forma de obtener varios subprocesos es compilar un archivo DLL en algo distinto de VBA que tenga una interfaz COM y llamarlo desde VBA.
INFORMACIÓN: descripciones y funcionamiento de los modelos de subprocesamiento OLE
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