Laufen mehrere asynchrone Abfragen mit ADODB - callbacks nicht immer brennen

Ich habe eine Excel-Arbeitsmappe, feuert drei Abfragen zu einer Datenbank zu füllen drei Tabellen auf ausgeblendeten Blätter, und läuft dann drei "aktualisieren" - Skripte zu ziehen diese Daten durch drei sichtbare Präsentation Blatt (eine pro Abfrage). Läuft dieser synchron ist ziemlich langsam: Die Gesamt-Zeit zu aktualisieren, ist die Summe der Zeit jeder der drei Abfragen, plus die Summe der Zeit für jede "aktualisieren" - Skript auszuführen.

Ich bin mir bewusst, dass VBA ist nicht multi-threaded ist, aber ich dachte, es würde sein möglich, um die Dinge zu beschleunigen ein wenig, indem er die Anfragen aus asynchron (so dass einige clean-up-Arbeit zu tun, während Sie ausgeführt wurden), und dann die Bevölkerung /aktualisieren Sie für jedes Blatt, die Daten kommen zurück.

Schrieb ich mein Skript wie folgt aus (beachten Sie, dass ich hatte, um entfernen Sie die Verbindungszeichenfolgen, query-strings etc und machen Sie die Variablen generische):

Private WithEvents cnA As ADODB.Connection
Private WithEvents cnB As ADODB.Connection
Private WithEvents cnC As ADODB.Connection

Private Sub StartingPoint()
    'For brevity, only listing set-up of cnA here. You can assume identical
    'set-up for cnB and cnC
    Set cnA = New ADODB.Connection

    Dim connectionString As String: connectionString = "<my conn string>"
    cnA.connectionString = connectionString

    Debug.Print "Firing cnA query: " & Now
    cnA.Open
    cnA.Execute "<select query>", adAsyncExecute  'takes roughly 5 seconds to execute

    Debug.Print "Firing cnB query: " & Now
    cnB.Open
    cnB.Execute "<select query>", adAsyncExecute  'takes roughly 10 seconds to execute

    Debug.Print "Firing cnC query: " & Now
    cnC.Open
    cnC.Execute "<select query>", adAsyncExecute  'takes roughly 20 seconds to execute

    Debug.Print "Clearing workbook tables: " & Now
    ClearAllTables
    TablesCleared = True
    Debug.Print "Tables cleared: " & Now
End Sub

Private Sub cnA_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnA records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly < 1 seconds to complete
    Debug.Print "Sheet1 tables received: " & Now
End Sub

Private Sub cnB_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnB records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly 2-3 seconds to complete
    Debug.Print "Sheet2 tables received: " & Now
End Sub

Private Sub cnC_ExecuteComplete(ByVal RecordsAffected As Long, ...)
    Debug.Print "cnC records received: " & Now
    'Code to handle the recordset, refresh the relevant presentation sheet here, 
    'takes roughly 5-7 seconds to complete
    Debug.Print "Sheet3 tables received: " & Now
End Sub

Typische erwartet debugger-Ausgabe:

Firing cnA query: 21/02/2014 10:34:22
Firing cnB query: 21/02/2014 10:34:22
Firing cnC query: 21/02/2014 10:34:22
Clearing tables: 21/02/2014 10:34:22
Tables cleared: 21/02/2014 10:34:22
cnB records received: 21/02/2014 10:34:26
Sheet2 tables refreshed: 21/02/2014 10:34:27
cnA records received: 21/02/2014 10:34:28
Sheet1 tables refreshed: 21/02/2014 10:34:28
cnC records received: 21/02/2014 10:34:34
Sheet3 tables refreshed: 21/02/2014 10:34:40

Den drei Abfragen zurückkommen können in unterschiedlicher Reihenfolge je nachdem, welche zuerst fertig ist, natürlich, so dass manchmal die typische Ausgabe sortiert ist anders - dies ist zu erwarten.

Manchmal jedoch ein oder zwei der cnX_ExecuteComplete Rückrufe nicht Feuer überhaupt. Nach einiger Zeit mit Debuggen, ich bin ziemlich sicher der Grund für dieses ist, dass, wenn ein recordset zurückgibt, während eine der Rückrufe wird derzeit ausgeführt wird, wird der Anruf nicht auftreten. Zum Beispiel:

  • Abfrage A, B und C alle Feuer zum Zeitpunkt 0
  • Abfrage Eine schließt das erste in time 3 cnA_ExecuteComplete feuert
  • Abfrage-B abgeschlossen, die zweite zum Zeitpunkt 5
  • cnA_ExecuteComplete noch läuft, so cnB_ExecuteComplete nie feuert
  • cnA_ExecuteComplete schließt bei Zeit 8
  • Abfrage-C schließt bei Zeit 10, cnC_ExecuteComplete feuert
  • Abfrage-C schließt bei Zeit 15

Bin ich richtig in meine Theorie, dass dies das Problem ist? Wenn dem so ist, ist es möglich, dies zu umgehen, oder den Anruf auf 'warten' bis der aktuelle code ausgeführt wurde, anstatt Sie einfach verschwinden?

Eine Lösung wäre, etwas zu tun, extrem schnell, während die cnX_ExecuteComplete Rückrufe (zB ein one-liner Set sheet1RS = pRecordset und einen check, um zu sehen, wenn Sie fertig sind, die noch vor der Ausführung der Skripte auffrischen synchron), so dass die chance für überschneidungen ist etwa null, sondern wollen wissen, ob es eine bessere Lösung gibt ersten.

  • +1 für gut geschriebene Frage
  • Schauen Sie auf Ihre Typical Expected debugger output:, wenn die cnA läuft 5 Sekunden dann die erste erwartete log-Vergangenheit Tables Cleared sollte eigentlich Debug.Print "cnA records received: " & Now. Du hast cnB records received ist ein bisschen irreführend nach deiner Logik. Ich bemerkt, Sie sagte, 5sec, 10sec, 20sec, aber nach, dass die erwartete sein sollte cnA, cnB, cnC. Können Sie dies erläutern?
  • Die Schätzungen sind grob, am besten - kann es am besten zu ignorieren diese Kommentare. Die Ausführungszeit der Abfragen zu variieren -, wo es heißt "ungefähr 5 Sekunden" können Sie nehmen, dass bedeutet, überall von 3 Sekunden bis 8, und "etwa 10 Sekunden" kann überall von 7 Sekunden auf 15. Es ist durchaus möglich, dass cnB beendet, bevor cnA oder cnC beendet, bevor cnB (oder etwas wirklich verlangsamt cnA und cnC beendet ist, Ehe es) - es werden alle Abfragen gegen einen externen server bedeutet, es ist völlig abhängig von der Netzwerk -, Gegenwart-server zu laden, record-locking, etc etc.
  • vielen Dank für die Klarstellung. Ich habe für Sie beantwortet sehen, ob das hilft
InformationsquelleAutor Kai | 2014-02-21
Schreibe einen Kommentar