Zum Inhalt springen

Multithreaded Exelreading


Lsteinme

Empfohlene Beiträge

Hi Leute, ich hab mal wieder ein Problem^^

Und zwar wollt ich im Geschäft eine relativ umfangreiche Exceltabelle auslesen (über 10k Zeilen)

Da das sehr lange dauert, im naitiv Ansatz (ein Thread der alle verwendeten Rows durchläuft) hab ich mir gedacht, versuchs doch mal Multithreaded.

Ich hab aktuell ne 3 Klassen Struktur aufgebaut:

eine Klasse die die Auslesung anfordert, die tut aber noch mehr, das funktioniert aber schon^^

eine Klasse namens ThreadControl die ich eigentlich nur als Zwischenklasse eingezogen hab, vielleicht braucht man sowas ja nochmal

und eine Klasse die den Thread darstellt.

Jetzt is aber das Problem: ich bekomm eine HResult Message (0x800A03EC) raus wenn das ganze anfängt zu laufen. Jetzt meine Frage, liegts ehr an meinem Code, oder erlaubt Excel kein auslesen mehrer Threads zugleich?

Gruß Lucas

Link zu diesem Kommentar
Auf anderen Seiten teilen

die Zentrale Frage: "erlaubt eine Exceldatei mehren Thread gleichzeitig das Lesen aus ihr" kann man übrigens auch ohne Code beantworten: Ja es funktioniert

Eine Excel-Datei für sich allein kann gar nichts erlauben, da muss schon ein ausführbares Programm dahinter stehen, das eine entsprechende Schnittstelle anbietet. Dass die Schnittstelle, die du da benutzt hast, threadsicher ist, heißt nicht, dass das für jede Schnittstelle gilt. Insoweit wäre es durchaus nützlich gewesen, wenn du wenigstens erwähnt hättest, wie du auf die Daten zugegriffen hast.

Link zu diesem Kommentar
Auf anderen Seiten teilen

ok,

Dim myExportSheet As Worksheet = CType(excelApp.Workbooks.Open(importfile).Sheets.Item(1), Worksheet)

war der code der mir das Worksheetliefert von dem ich auslesen will, ob das jetzt geklappt hat weiß ich noch net 3 von 4 Threads blieben ganz Blank im result, was nicht sein sollte, aber logische Gründe haben könnte.

Von miraus hier mal der relevante Code für die Threadbearbeitung:

Aufrufende Klasse die jede menge tut was eigentlich mit dem problem nix zu tun hat:


sub FUNKTIONSNAME()

Dim myExportSheet As Worksheet = CType(excelApp.Workbooks.Open(importfile).Sheets.Item(1), Worksheet)

...

With treadcontrol

                        Dim wieviele As Integer = myExportSheet.UsedRange.Rows.Count

                        Dim rest As Integer = wieviele Mod .anzCores

                        Dim startvalues(.anzCores - 1) As Integer

                        Dim endvalues(.anzCores - 1) As Integer


                        Dim currententry As Integer = 0


                        For i As Integer = 0 To startvalues.Count - 1

                            startvalues(i) = currententry + 1

                            endvalues(i) = CInt(Fix(wieviele / .anzCores)) + currententry

                            currententry = endvalues(i)

                            If ((i + 1).Equals(.anzCores)) Then

                                endvalues(i) = endvalues(i) + (wieviele Mod (.anzCores))

                            End If

                            treadcontrol.createNewSearchingThread(startvalues(i), endvalues(i), myExportSheet, i)

                        Next

                    End With

End Sub

'wenn hier alles gescheit ankommt is der rest egal

Public Function resumeRM_Import(e As List(Of UncastedProjektmitarbeiter)) As Boolean Handles 

...

 End Function

Klasse ThreadControl:
Public Class Threadcontrol

    Public Event excelloadfinished(list As List(Of UncastedProjektmitarbeiter))

    'this is a current version, if the Working PCs get more cores, increase this number

    Public anzCores As Integer = 4

    Private _oPX_Logic As OPX_Logic

    Private finished(anzCores - 1) As Boolean

    Dim ExcelLoadObject(anzCores - 1) As ExcelLoadThread

 Sub New(oPX_Logic As OPX_Logic)

        ' TODO: Complete member initialization 

        _oPX_Logic = oPX_Logic

    End Sub



    Sub createNewSearchingThread(startvalues As Integer, endvalues As Integer, myExportSheet As Worksheet, i As Integer)

        ExcelLoadObject(i) = New ExcelLoadThread(startvalues, Me, myExportSheet, i, endvalues)

        Dim excelloadThread As New Threading.Thread(AddressOf ExcelLoadObject(i).startreading)

        excelloadThread.Start()

    End Sub


    Public Sub threadfinishedHandler(e As Integer)

        finished(e) = True

        Dim all As Boolean = True

        For Each index As Boolean In finished

            If (index = False) Then

                all = False

                Exit For

            End If

        Next

        If (all) Then

            Dim newList As New List(Of UncastedProjektmitarbeiter)

            For index As Integer = 0 To anzCores - 1

                newList.AddRange(ExcelLoadObject(index).mitarbeiters)

            Next

            RaiseEvent excelloadfinished(newList)

        End If

    End Sub


End Class

ThreadKlasse:

Imports Microsoft.Office.Interop

Imports System.Globalization


Public Class ExcelLoadThread

    Public Event threadfinished(e As Integer)

    Public mitarbeiters As New List(Of UncastedProjektmitarbeiter)

    Dim mysheet As Worksheet

    Dim startrow As Integer

    Dim endrow As Integer

    Dim index As Integer

    Sub New(startrow As Integer, control As Threadcontrol, sheet As Excel.Worksheet, index As Integer, Optional endrow As Integer = 0)

        Me.startrow = startrow

        Me.endrow = endrow

        Me.index = index

        mysheet = sheet

        AddHandler threadfinished, AddressOf control.threadfinishedHandler

    End Sub


    Sub startreading()

        For Each r As Excel.Range In mysheet.Range(mysheet.Cells(startrow, 1), mysheet.Cells(endrow, 16))

            Dim Rescol As String = CStr(CType(r.Cells(1, 3), Excel.Range).Value)

            If (String.IsNullOrEmpty(Rescol) OrElse Rescol.Equals("")) Then

                Exit For

            End If


            If (Rescol.Contains("Ressource") Or Rescol.Contains("Gen.Res.")) Then

                Continue For

            End If

            Dim BEcol As String = CStr(CType(r.Cells(1, 5), Excel.Range).Value)

            If String.IsNullOrEmpty(BEcol) OrElse Not BEcol.Contains("BE") Then

                Continue For

            End If

            Dim datum As Date

            Date.TryParseExact(CStr(CType(CType(r, Excel.Range).Cells(1, 16), Excel.Range).Value), "dd.MM.yyyy", New CultureInfo("de-DE"), Globalization.DateTimeStyles.None, datum)

            Dim rollnumber As String = CStr(CType(CType(r, Excel.Range).Cells(1, 6), Excel.Range).Value)

            mitarbeiters.Add(New UncastedProjektmitarbeiter(Rescol, BEcol, rollnumber, datum))

        Next

        RaiseEvent threadfinished(index)

    End Sub


End Class

Hoffe das war nun erhellend ich halts er für verwirrend, weswegen ich den Code nicht gleich gepostet hab^^

Kurz zur Erklärung: das Programm liest eine speziellformatierte Exceldatei ein, parsed die Daten, und speichert sie in der DB (falls sich was geändert hat) im code steht jetzt nur das Excelauslesen

Gruß Lucas

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist zwar eine nette Behauptung, stimmt aber nicht.

LINQ arbeitet intern quasi genau so wie ausgeschriebene Schleifen. In sehr komplexen Abläufen in der Schleife kann man etwas Geschwindigkeit gewinnen, aber in einer einfachen Schleife wird da kein Unterschied sein.

Link zu diesem Kommentar
Auf anderen Seiten teilen

c# - LINQ performance FAQ - Stack Overflow

LINQ vs Loop – A performance test | Circles and Crosses

Martin's Blog: C#: LINQ and Foreach performance comparison

Quellen für Gegenbehauptungen?

PS: Ich möchte LINQ hier nicht schlecht reden - es ist eine der geilsten Techniken im .NET Framework überhaupt. Es geht hier rein um das Thema "Einfache Datenabfrage - Performance Unterschiede"

PPS: Wer ganz genau wissen möchte wie LINQ arbeitet: http://www.codeproject.com/Articles/383749/How-does-it-work-in-Csharp-Part-3-Csharp-LINQ-in-d

PPPS: SilentDemise, auch mal mit dem herkömmlichen Weg probiert? Am besten du postest hier mal deinen verwendeten Code - oder in einem neuen Thread (das alles gehört hier eigentlich gar nicht hin). Es würde mich sehr überraschen, wenn bei einfachen Datenstrukturen LINQ eine erhöhte Performance ans Tageslicht bringen würde.

Bearbeitet von Felix91
Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist ja alles schön und gut aber passt leider nicht auf das hier vorliegende Problem.

Wenn du eine Linq Abfrage auf eine einfache Collection machst dann wird da natürlich intern auch in einer Schleife oder Iterator drüber gelaufen.

Wenn du aber stattdessen eine Linq Abfrage auf eine Datenquelle machst welche zum Beispiel eine Datenbank verbindet, läuft Linq da nicht in einer Schleife über deine Tabellen sondern generiert anhand deiner Linq Abfrage eine entsprechende SQL Abfrage und holt sich darüber die gewünschten Daten.

Um jetzt zu wissen was genau bei einer Excel Abfrage passiert muss man wissen welche Möglichkeiten Excel zur Verfügung stellt und wie der DataProvider dafür genau arbeitet. Aber ich wage jetzt mal zu behaupten das da auch nicht einfach in einer Schleife Zelle für Zelle ausgelesen wird.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Moment - ich bin davon ausgegangen, dass wir hier die ganze Zeit über linq to object reden. Es ging um eine Datei auslesen - nicht um eine echte Daten Verbindung.

Da ich mich nicht im Detail mit interop auskenne, kann ich hierzu jetzt keine qualifizierte Aussage machen. Spontan würde ich behaupten, der DataProvider gibt einfach nur eine collection zurück - womit wir wieder bei linq to object wären. Damit wäre es egal, wie der DataProvider die Daten liest, er hat sie zum Zeitpunkt der Laufzeit im Speicher und die Last liegt in der Anwendung.

Das würde ich jetzt dadurch begründen, dass es freie Bibliotheken gibt, die linq to excel ermöglichen (z.B. linqtoexcel - Use LINQ to retrieve data from spreadsheets. - Google Project Hosting ). Hierbei wird dann per OleDB auf die Excel Datei zugegriffen. Dass Linq hier schneller ist, ist ein anderes Thema.

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Felix91

Deine genannten Beispiele sind knapp 3 Jahre alt.

Ich habe das Beispiel LINQ vs Loop – A performance test | Circles and Crosses genommen und schnell nachgestellt.

Ergebnis:

Loop 00:00:00.0600000

linq 00:00:00.1700000

Loop ist hier immer noch etwas schneller, aber das Ergebnis weicht sehr von dem Beispiel ab.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...