Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien
Toon posts:

VB.Net Multithreading om UI responsive te houden

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

Verwijderd

Topicstarter
Ik ben een app aan het maken in VB.net met behulp van VS2005. Nu maak ik daarin een wizard die een nieuw project voor je creeert. Een nieuw project roept een SP aan op een SQL 2005 server en creeert een database. Dat gebeurt in twee stappen. Eerst de database aanmaken en dan een hoop SP en tabellen kopieren van een andere database. Deze twee stappen wil ik uitvoeren in een thread om ervoor te zorgen dat mijn UI (form) in VB niet hangt en een lopende progress bar laat zien.

Mijn mijn form ziet eruit als:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Imports System.Data.OleDb
Imports System.Threading
Public Class NewProject2
    Private t As Thread
    Private myDbConnection As dbConnection
    Private Sub btnNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNext.Click
        spNewProject.Visible = True
        If txtNewProject.Text = "" Then
            progress("You must provide a project name", 2, "A project name is required to create a new project database on the analysis server")
            Exit Sub
        End If

        myDbConnection = New dbConnection()
        AddHandler myDbConnection.message, AddressOf connMessage
        AddHandler myDbConnection.connected, AddressOf createNewProject

        t = New Thread(AddressOf myDbConnection.connect)
        t.Start()

    End Sub


De eventhandler message zorgt ervoor dat connection events van de class DBconnection in de UI verwerkt worden die draait in de main thread:
code:
1
2
3
4
5
6
7
8
9
10
11
Private Delegate Sub connMessageDelegate(ByVal status As String, ByVal code As Integer, ByVal detailStatus As String)
    Private Sub connMessage(ByVal status As String, ByVal code As Integer, Optional ByVal detailStatus As String = "")
        If Me.InvokeRequired Then
            Dim args As Object() = {status, code, detailStatus}
            Dim conn_message_delegate As connMessageDelegate
            conn_message_delegate = AddressOf connMessage
            Me.Invoke(conn_message_delegate, args)
        Else
            progress(status, code, detailStatus)
        End If
    End Sub


De class db connection ziet er als volgt uit:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Imports System.Data.OleDb
Public Class dbConnection
    Public connstring As String = "Provider=SQLOLEDB;blablabla..."
    Public Event message(ByVal status As String, ByVal code As Integer, ByVal detailStatus As String)
    Public Event connected()
    Public connection As OleDbConnection = New OleDbConnection(connstring)
    Public Sub connect()
        Try
            RaiseEvent message("Trying to connect to database", 3, "")
            connection.Open()
            RaiseEvent message("Connection succeeded", 4, "")
            RaiseEvent connected()
        Catch ex As Exception
            RaiseEvent message("Connection to database failed", 1, ex.Message)
        End Try
    End Sub
End Class


Na het testen van de DB connection (door te connecten) wil ik een nieuw process in de thread starten die de gehele db aanmaakt, stored procedures overzet etc. Ik snap dat dat in dezelfde thread zou kunnen waar in de DB connection maak, echter wil ik deze later uiteraard hergebruiken voor andere doeleinden.

Iemand enig idee hoe ik dit voor elkaar kan krijgen? Wellicht pak ik het e.a. helemaal verkeerd aan. Tips zijn welkom.

  • titan_pi8
  • Registratie: Januari 2004
  • Laatst online: 11-11 23:10
Heb je ook al aan Application.doEvents() gedacht?

Verwijderd

Topicstarter
Ik zie al wat ik verkeerd doe. Ik gebruik een functie van een class in een main thread. In mijn geval myDbConnection.connect. Uiteraard kan ik ook een private sub aanmaken en vanuit de main de gehele sub in een thread stoppen.

Zie voorbeeld:
code:
1
2
3
4
5
6
7
8
9
10
11
12
    Private Sub btnNext_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNext.Click
        spNewProject.Visible = True
        If txtNewProject.Text = "" Then
            progress("You must provide a project name", 2, "A project name is required to create a new project database on the analysis server")
            Exit Sub
        End If
        t = New Thread(AddressOf start)

        't = New Thread(AddressOf myDbConnection.connect)
        t.Start()

    End Sub


en nu in een sub start de gehele afhandeling doen:

code:
1
2
3
4
5
6
7
8
    Private Sub start()
        myDbConnection = New dbConnection()
        AddHandler myDbConnection.message, AddressOf connMessage
        AddHandler myDbConnection.connected, AddressOf createNewProject
        myDbConnection.connect()
        ..... met hierna alle andere handelingen..

    End Sub

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 30-11 19:45

TeeDee

CQB 241

titan_pi8 schreef op donderdag 30 augustus 2007 @ 10:33:
Heb je ook al aan Application.doEvents() gedacht?
Correct me if I'm wrong, maar dat is over het algemeen toch niet zo netjes?

Heart..pumps blood.Has nothing to do with emotion! Bored


Verwijderd

Topicstarter
Bovendien wil ik graag met threading werken voor de ervaring.

  • sig69
  • Registratie: Mei 2002
  • Laatst online: 10:37
Of kijk eens naar de asynchrone methods van de SqlCommand class (Begin... en End...)

Roomba E5 te koop


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:52
Kijk eens naar de BackGroundWorker component. Deze laat je toe om relatief makkelijk 'iets' op ee nandere thread uit te voeren, en geeft je de mogelijkheid om de vooruitgang te gaan tonen (met een bepaald event), en zorgt ervoor dat die event ook op de juiste thread (bv de UI thread als je met een progressbar werkt) uitgevoerd wordt.

Application.DoEvents is niet echt een oplossing in dit geval ...

https://fgheysels.github.io/


  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Het beste kun je de connectie test code in dezelfde thread houden als de UI. Immers zolang het programma probeert te connecten, mag er toch niets veranderd worden.

Na de controle kom je in het gedeelte waar je meerdere threads kunt starten. Het aanmaken van de database zelf kan uiteraard nog niet in een thread (andere onderdelen zijn immers afhankelijk van de nieuwe database). Nadat de database is aangemaakt, kun je een thread starten welke elk type object aanmaakt (view, tables, stored procedures, fucntions, users). Wel moet je dan bijhouden welke taken al zijn voltooid. Zodra bijvoorbeeld de tables aangemaakt zijn, kun je de database gaan vullen met standaard data.

Je zou dan een soort interface krijgen zoals de DTS import/export.

Echter met de threads komen vaak ook de asynchrone methodes om de hoek kijken zoals sig69 al aangeeft. Immers het heeft geen zin om threads aan te maken, als de thread weer procedureel wordt afgehandeld.

Voordat je begint moet je dus goed bedenken welke onderdelen als een aparte thread moeten starten en welke beter als asynchroon kunnen worden geschreven. Verder vind de GUI het ook niet leuk als een control wordt benaderd uit een andere thread (update progressbar bijvoorbeeld). Meestal wordt dan Control.CheckForIllegalCrossThreadCalls op false gezet, maar een betere methode is aan asynchrone call welke zelf de thread start en de End.. vervolgens de gui wijzigingen doorvoert.

Threading an Asynchrone calls zijn per definitie al lastioge concepten, dus adviseer ik je om de MSDN documentatie omtrend deze onderwerpen goed door te lezen.

If it isn't broken, fix it until it is..


  • PolarBear
  • Registratie: Februari 2001
  • Niet online
Niemand_Anders schreef op donderdag 30 augustus 2007 @ 11:25:
Echter met de threads komen vaak ook de asynchrone methodes om de hoek kijken zoals sig69 al aangeeft. Immers het heeft geen zin om threads aan te maken, als de thread weer procedureel wordt afgehandeld.
Niet perse. Juist met de Backgroundworker die whoami al adviseerde kan je makkelijk een stuk code in een andere thread runnen om de UI responsive te houden. Dat was het doel, niet zoveel mogelijk dingen te gelijk doen.

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
Niemand_Anders schreef op donderdag 30 augustus 2007 @ 11:25:
Het beste kun je de connectie test code in dezelfde thread houden als de UI. Immers zolang het programma probeert te connecten, mag er toch niets veranderd worden.
En als tijdens de bewerkingen de verbinding wegvalt dan? Je moet zowieso dit in je andere thread ook controleren.
Echter met de threads komen vaak ook de asynchrone methodes om de hoek kijken zoals sig69 al aangeeft. Immers het heeft geen zin om threads aan te maken, als de thread weer procedureel wordt afgehandeld.
Bollox.
Verder vind de GUI het ook niet leuk als een control wordt benaderd uit een andere thread (update progressbar bijvoorbeeld). Meestal wordt dan Control.CheckForIllegalCrossThreadCalls op false gezet ...
Ik denk dat elke serieuze ontwikkelaar een oplossing zoekt ipv de controle uit te schakelen.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • Serpie
  • Registratie: Maart 2005
  • Laatst online: 01-07-2023
farlane schreef op donderdag 30 augustus 2007 @ 12:01:
Ik denk dat elke serieuze ontwikkelaar een oplossing zoekt ipv de controle uit te schakelen.
Lijkt mij ook, via een Delegate en de Control.Invoke method. Zoals Whoami al aangaf wordt dit door de backgroundworker al voor je geregeld.

Verwijderd

Topicstarter
De backgroundworker is een handige oplossing. Echter, op het moment dat je meer dan alleen de progress in een percentage will teruggeven, wordt het onhandiger. In mijn functie (en de nog te bouwen functies) wil ik zelf bepalen welke functies e.d. waar in de thread worden aangeroepen. Het kan zijn dat een thread een complexe bewerking heeft en dat deze bewerking allerlei info op de UI moet veranderen (status teruggeven, buttons enabled/disable, progressbars, etc).

Als je dan zelf de thread maakt ipv de backgroudworker te gebruiken, kun je al deze events er zelf inzetten. Dus meer flexibiliteit.

Klopt dit, of heb ik de verkeerde backgroundworker documentatie en examples bekeken?

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:52
Verwijderd schreef op donderdag 30 augustus 2007 @ 13:24:
De backgroundworker is een handige oplossing. Echter, op het moment dat je meer dan alleen de progress in een percentage will teruggeven, wordt het onhandiger. In mijn functie (en de nog te bouwen functies) wil ik zelf bepalen welke functies e.d. waar in de thread worden aangeroepen. Het kan zijn dat een thread een complexe bewerking heeft en dat deze bewerking allerlei info op de UI moet veranderen (status teruggeven, buttons enabled/disable, progressbars, etc).

Als je dan zelf de thread maakt ipv de backgroudworker te gebruiken, kun je al deze events er zelf inzetten. Dus meer flexibiliteit.

Klopt dit, of heb ik de verkeerde backgroundworker documentatie en examples bekeken?
Dat kan je toch ook ? Je kan toch iedere keer als het nodig is die ProgressChanged event raisen, en dan de nodige actie nemen.
Indien het toch nog niet 'goed' genoeg is, dan zal je je eigen 'Task - achtige' class moeten creeëren. (De backgroundworker maakt gebruik van het task pattern; je kan je eigen class maken met alle nodige toeters en bellen volgens ditzelfde patroon).

https://fgheysels.github.io/


Verwijderd

Topicstarter
Ik snap wat je bedoelt. Ik ga de documentatie van die task dingen bekijken. Thanks.
Pagina: 1