Многопоточность в VBA - PullRequest
54 голосов
/ 19 апреля 2011

Кто-нибудь здесь знает, как заставить VBA запускать несколько потоков? Я использую Excel.

Ответы [ 7 ]

55 голосов
/ 19 апреля 2011

Не может быть сделано изначально с VBA.VBA построен в однопоточной квартире.Единственный способ получить несколько потоков - это создать DLL в чем-то отличном от VBA, который имеет интерфейс COM, и вызвать его из VBA.

INFO: описания и работа моделей потоков OLE

20 голосов
/ 02 декабря 2014

Как вы, вероятно, узнали, VBA изначально не поддерживает многопоточность, но.Существует 3 метода для достижения многопоточности:

  1. COM / dll - например, C # и класс Parallel для запуска в отдельных потоках
  2. Использование работника VBscriptпотоки - запуск кода VBA в отдельных потоках VBscript
  3. Использование рабочих потоков 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
13 голосов
/ 18 ноября 2011

Я искал что-то подобное, и официальный ответ - нет.Тем не менее, мне удалось найти интересную концепцию Дэниела на ExcelHero.com.

По сути, вам нужно создать рабочие скрипты vbscript для выполнения различных задач, которые вам нужны, и сделать так, чтобы они отчитывались о превосходстве.Для того, что я делаю, получая данные HTML с различных веб-сайтов, это прекрасно работает!

Взгляните:

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

6 голосов
/ 04 июля 2015

Я добавляю этот ответ, поскольку программисты, переходящие на VBA с более современных языков и ищущие переполнение стека для многопоточности в VBA, могут не знать о паре нативных подходов VBA, которые иногда помогают компенсировать отсутствие настоящей многопоточности в VBA.

Если мотивация многопоточности состоит в том, чтобы иметь более отзывчивый пользовательский интерфейс, который не зависает при выполнении долго выполняющегося кода, у VBA есть пара низкотехнологичных решений, которые часто работают на практике:

1) Пользовательские формы могут отображаться немодально, что позволяет пользователю взаимодействовать с Excel, пока форма открыта.Это можно указать во время выполнения, установив для свойства пользовательской формы ShowModal значение false, или можно сделать это динамически в качестве загрузок from, поместив строку

UserForm1.Show vbModeless

в событие инициализации пользовательской формы.

2) Заявление DoEvents.Это приводит к тому, что VBA передает управление ОС для выполнения любых событий в очереди событий, включая события, созданные в Excel.Типичный вариант использования - обновление диаграммы во время выполнения кода.Без DoEvents диаграмма не будет перекрашиваться до тех пор, пока не будет запущен макрос, но с Doevents вы можете создавать анимированные диаграммы.Разновидностью этой идеи является обычная хитрость создания индикатора прогресса.В цикле, который должен выполняться 10000000 раз (и управляется индексом цикла i ), вы можете иметь следующий фрагмент кода:

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

Ничего из этого не является многопоточным, нов некоторых случаях это может быть адекватный клудж.

2 голосов
/ 13 апреля 2017

Я знаю, что этот вопрос относится к 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

Реализация поддержки параметров должна быть достаточно простой, однако возврат значений затруднен.

1 голос
/ 14 июня 2018

Как уже было сказано, VBA не поддерживает многопоточность.

Но вам не нужно использовать C # или vbScript для запуска других рабочих потоков VBA.

Я использую VBA для создания рабочих потоков VBA .

Сначала скопируйте книгу makro для каждого потока, который вы хотите запустить.

Затем вы можете запустить новые экземпляры Excel (запущенные в другомThread) просто путем создания экземпляра Excel.Application (чтобы избежать ошибок, я должен установить новое приложение видимым).

Чтобы фактически запустить какую-либо задачу в другом потоке, я могу затем запустить макрос в другом приложении.с параметрами из главной рабочей книги.

Чтобы вернуться в основной поток рабочей книги без ожидания, я просто использую 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
0 голосов
/ 29 апреля 2018
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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...