Toon posts:

[VB.NET] Backgroundworker busy

Pagina: 1
Acties:
  • 136 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
Mijn programma hangt bij het bewerken en importeren van bepaalde data. FunctionX bewerkt de data en een subroutine SavetoDatabase() schrijft de data naar de database. Het verwerken van ongeveer 4500 regels nam op mijn computer 10 seconden in beslag. Ongeveer 1 seconde voor FunctionX en de rest voor het inserten van de gegevens in de database (dat is duidelijk de vertragende factor). Als oplossing kan ik de backgroundworker class gebruiken, zoals in het volgende voorbeeld geillustreerd (SavetoDatabase() wordt afgehandeld door de backgroundworker).

Visual Basic .NET:
1
2
3
4
5
6
    Dim WithEvents BackgroundWorker As New BackgroundWorker   
------------------
        Do
            line = sr.ReadLine()
            If FunctionX(line) = True Then BackgroundWorker.RunWorkerAsync()
        Loop Until sr.EndOfStream = True


Visual Basic .NET:
1
2
3
4
5
Private Sub backgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker.DoWork

SavetoDatabase() 'database insert actie

End Sub


In dit geval krijg ik echter een Invalid Operation Exception (This BackgroundWorker is currently busy and cannot run multiple tasks concurrently). De reden is natuurlijk dat de code de DoWork event opnieuw wil uitvoeren terwijl deze nog busy is. De SavetoDatabase method is nog lang niet klaar voor de volgende line al weer moet worden verwerkt.

De oplossing is volgens mij om de hele Do/Loop in het DoWork event te plaatsen (op deze manier ondervang je ook het probleem van de FunctieX vertraging). Het probleem is echter dat het StreamReader object in een aantal gevallen reeds een bepaalde readline positie bevat en dit object dus moet worden doorgegeven aan het DoWork event. Ik heb alleen geen idee hoe ik dit moet doen.

Zoals ik in gedachte heb dat het moet worden:

Visual Basic .NET:
1
2
3
4
    Dim WithEvents BackgroundWorker As New BackgroundWorker   
------------------

BackgroundWorker.RunWorkerAsync()


Visual Basic .NET:
1
2
3
4
5
6
Private Sub backgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker.DoWork
 Do
            line = sr.ReadLine()
            If FunctionX(line) = True Then SavetoDatabase()
        Loop Until sr.EndOfStream = True
End Sub


Is dit een goede oplossing voor het gebruik van de backgroundworker of hebben jullie andere suggesties? En zo ja, hoe moet ik het StreamReader object doorgeven aan het DoWork event?

Verwijderd

Topicstarter
Ik heb het op de volgende manier opgelost. Het hele proces is nu echter wel 50% trager. Als iemand nog suggesties heeft hoor ik het graag.

Visual Basic .NET:
1
2
3
4
5
6
7
8
9
    Dim WithEvents BackgroundWorkerDatabase As New BackgroundWorker 
------------------
        Do
            line = sr.ReadLine()
            If FunctionX(line) = True Then BackgroundWorkerDatabase.RunWorkerAsync()
            While Me.BackgroundWorkerDatabase.IsBusy
                Application.DoEvents()
            End While
        Loop Until sr.EndOfStream = True

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Is het geen idee om eerst eens naar dat save - proces te kijken ? Hoe ziet dat er uit ?
9 seconden om 4500 rows te inserten / updaten is nl. -denk ik- nogal lang...

[ Voor 30% gewijzigd door whoami op 11-05-2007 12:05 ]

https://fgheysels.github.io/


Verwijderd

Topicstarter
Ik heb het nog een stuk verbeterd. De backgroundworker is nergens voor nodig. Onderstaand voorbeeld werkt prima.

Visual Basic .NET:
1
2
3
4
5
        Do
            line = sr.ReadLine()
            If FunctionX(line) = True Then SavetoDatabase()
            Application.DoEvents()
        Loop Until sr.EndOfStream = True

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 21:34

mulder

ik spuug op het trottoir

Volgens mij moet je in her DoWork event de DoWorkEventArgs property Result op True zetten. (In je eerste voorbeeld) Kijken of de Worker klaar is doe je in het RunWorkerCompleted event.

oogjes open, snaveltjes dicht


Verwijderd

Topicstarter
whoami schreef op vrijdag 11 mei 2007 @ 12:05:
Is het geen idee om eerst eens naar dat save - proces te kijken ? Hoe ziet dat er uit ?
9 seconden om 4500 rows te inserten / updaten is nl. -denk ik- nogal lang...
Bedankt voor de suggestie. Ik zal de oorzaak van de vertraging in dit gedeelte proberen te localiseren. Waarschijnlijk ligt het aan de 16 parameters die ik toevoeg aan het command. Een stored procedure kan volgens mij ook nog voor snelheidswinst zorgen, maar daar moet ik me nog even in verdiepen.

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 21:34

mulder

ik spuug op het trottoir

Verwijderd schreef op vrijdag 11 mei 2007 @ 12:10:
[...]


Bedankt voor de suggestie. Ik zal de oorzaak van de vertraging in dit gedeelte proberen te localiseren. Waarschijnlijk ligt het aan de 16 parameters die ik toevoeg aan het command. Een stored procedure kan volgens mij ook nog voor snelheidswinst zorgen, maar daar moet ik me nog even in verdiepen.
16 paramters is niks en ook een SP gaat je geen snelheids winst op leveren. Hoe insert je dat data, regel voor regel en/of maak je steeds een nieuwe connectie?

oogjes open, snaveltjes dicht


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Waarom denk je dat een SP voor snelheidswinst kan zorgen ?
Waarom zou het aan die 16 parameters liggen ?

Liggen er veel indexen op die tabel ?
Welk type DB gebruik je ? In geval van SQL Server, op welk veld ligt je clustered index?
Hoe insert / update je die rijen precies ? Ga je voor iedere insert / update een nieuwe connectie naar de DB openen ? Indien ja, dan zal dit zowiezo een vertragende factor zijn.

[ Voor 8% gewijzigd door whoami op 11-05-2007 12:18 ]

https://fgheysels.github.io/


Verwijderd

Topicstarter
Bedankt voor de tip met betrekking tot het aanmaken van een connection. Ik maak inderdaad voor elke row een nieuwe connectie aan. Ik heb dit veranderd naar enkel één connectie voor alle rows en het hele proces van 10 seconden is nu gehalveerd naar 5 seconden. Beide bedankt.

edit: Ik gebruik inderdaad SQL server (express edition). Ik heb 17 columns waarvan de eerste een identity specification en primary key heeft. Ik gebruik een sql insert command met 16 parameters om een row te inserten.

[ Voor 28% gewijzigd door Verwijderd op 11-05-2007 13:25 ]


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 21:34

mulder

ik spuug op het trottoir

Misschien is het in dit geval interessant om naar het BULK INSERT commando te kijken.

oogjes open, snaveltjes dicht


Verwijderd

Topicstarter
Don Facundo schreef op vrijdag 11 mei 2007 @ 13:33:
Misschien is het in dit geval interessant om naar het BULK INSERT commando te kijken.
Bedankt voor de suggestie, maar ik ben tevreden met het huidige resultaat. De MSDN pagina van bulk insert ziet er ingewikkeld uit en deze method wordt daarnaast niet zo vaak uitgevoerd.

Ik heb nog een kleine andere vraag: Waarom is het niet noodzakelijk om een instance van form2 aan te maken voor ik form2.show() aanroep? Visual basic does dit waarschijnlijk automatisch, maar waar staat dit ergens ingesteld?

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 21:34

mulder

ik spuug op het trottoir

Verwijderd schreef op vrijdag 11 mei 2007 @ 13:54:
[...]
Ik heb nog een kleine andere vraag: Waarom is het niet noodzakelijk om een instance van form2 aan te maken voor ik form2.show() aanroep? Visual basic does dit waarschijnlijk automatisch, maar waar staat dit ergens ingesteld?
Dat zou niet moeten kunnen imho, staat er niet ergens stiekem wel een initializer? Heel misschien ligt het aan een van de Option Base/Explicit/... opties die dit mogelijk zou kunnen maken maar dat lijkt mij toch onwaarschijnlijk.

oogjes open, snaveltjes dicht


Verwijderd

Topicstarter
Ik heb option strict / explicit aanstaan. Je kan het zelf proberen:
Maak een nieuw vb project aan.
Voeg een form toe (form2).
In de form_load van form1: form2.Show()

Het resultaat is dat form2 zal openen bij het uitvoeren van je applicatie.

  • MacWolf
  • Registratie: Januari 2004
  • Laatst online: 06-09-2024
Verwijderd schreef op vrijdag 11 mei 2007 @ 13:54:
[...]


Bedankt voor de suggestie, maar ik ben tevreden met het huidige resultaat. De MSDN pagina van bulk insert ziet er ingewikkeld uit en deze method wordt daarnaast niet zo vaak uitgevoerd.

Ik heb nog een kleine andere vraag: Waarom is het niet noodzakelijk om een instance van form2 aan te maken voor ik form2.show() aanroep? Visual basic does dit waarschijnlijk automatisch, maar waar staat dit ergens ingesteld?
oops, verkeerde antwoord, even verwijderd

Het logische antwoord zou zijn dat System.Windows.Forms.Form een abstracte class is (dus je kan de class niet direct gebruiken, alleen instances die ervan gemaakt zijn). Je eigen forms hebben deze class overerfd en zijn dus instances van de abstracte class.

Maak bijv. zelf eens deze class:

code:
1
2
3
4
5
Public MustInherit Class Test
    Public Shared Sub TestSub(ByVal Message As String)
        MsgBox(Message)
    End Sub
End Class


Maak daarnaast een class Test1 die overerft van Test. In het Load event van je mainform kan je nu Test1.TestSub("blaat") aanroepen zonder een instance te maken.

[ Voor 73% gewijzigd door MacWolf op 11-05-2007 16:48 ]

Microsoft Windows: A thirty-two bit extension and graphical shell to a sixteen-bit patch to an eight-bit operating system originally coded for a four-bit microprocessor which was written by a two-bit company that can't stand one bit of competition.


  • D-Raven
  • Registratie: November 2001
  • Laatst online: 16-10 10:47
Overigens ter informatie: je kunt aan de RunWorkerAsync() een object argument meegeven.
Welke dan in het DoWork event weer op te halen is bij e.Argument.

Dan kun je dus dit doen:

C#:
1
2
3
4
5
6
bwFiller.RunWorkerAsync(StreamReaderInstance);

bwFiller_DoWork(object sender, DoWorkEventArgs e)
{
   StreamReader reader = (StreamReader)e.Argument;
}


En dan in vb.Net :P

Verwijderd

Topicstarter
Hoe schrijf je de connectionstring zodat deze het bestand in dezelfde directory als de executable gebruikt?

Mijn huidige connectionstring is als volgt:

code:
1
con.ConnectionString = "Data Source=.\SQLEXPRESS;AttachDbFilename='C:\Documents and Settings\Jan\My Documents\Visual Studio 2005\Projects\HistoryAnalysisTool\HistoryAnalysisTool\Data.mdf';Integrated Security=True;User Instance=True"


Ik heb verschillende dingen geprobeerd, zoals het weglaten van het path of \Data.mdf. Dit mag echter niet baten.

[ Voor 92% gewijzigd door Verwijderd op 11-05-2007 19:37 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Verwijderd schreef op vrijdag 11 mei 2007 @ 13:54:
[...]


Bedankt voor de suggestie, maar ik ben tevreden met het huidige resultaat. De MSDN pagina van bulk insert ziet er ingewikkeld uit en deze method wordt daarnaast niet zo vaak uitgevoerd.

Ik heb nog een kleine andere vraag: Waarom is het niet noodzakelijk om een instance van form2 aan te maken voor ik form2.show() aanroep? Visual basic does dit waarschijnlijk automatisch, maar waar staat dit ergens ingesteld?
Dit is in VB6 idd zo hoor ik van collega's, maar in VB.NET ??
Het logische antwoord zou zijn dat System.Windows.Forms.Form een abstracte class is (dus je kan de class niet direct gebruiken, alleen instances die ervan gemaakt zijn). Je eigen forms hebben deze class overerfd en zijn dus instances van de abstracte class.
Da's niet zo.
Je kan eens kijken in de MSDN en daar zal je zien dat Form niet abstract is. Je moet trouwens maar eens proberen om zelf een Form te maken , die je als abstract definieert. Van die form moet je eens een andere form overerven, en die ge-inherite form moet je eens in de designer bekijken. :)

[ Voor 32% gewijzigd door whoami op 11-05-2007 19:58 ]

https://fgheysels.github.io/


  • Serpie
  • Registratie: Maart 2005
  • Laatst online: 01-07-2023
whoami schreef op vrijdag 11 mei 2007 @ 19:56:
[...]
Dit is in VB6 idd zo hoor ik van collega's, maar in VB.NET ??
In VB.NET 2.0 kan het weer, geen idee waarom ze dat gedaan hebben maarja.
Pagina: 1