Opération inter-threads non valide [Résolu]

f1f58e8c06b2a61ce13e0c0aa9473a72 - 13 août 2017 à 11:29 - Dernière réponse : Whismeril 10526 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 13 décembre 2017 Dernière intervention
- 13 août 2017 à 15:17
Bonjour,
Mon logiciel consiste à actualiser les donné de ma DataGridView de ma base de donner en temps réel, j'ai donc le code si dessous que je mets dans un BackgroundWorker qui est activé par un timer toutes les 2 sec (RunWorkerCompleted timer.start). Le problème c'est que j'ai une erreur "Opération inter-threads non-valide", j'ai un peu cherche sur internet et la seule solution que j'ai trouvé est de faire 'Invoke' sauf que je ne sais pas comment faire. Ou si vous avez une autre solution, je suis tout ouie.

Code BackgroundWorker :
   Try

            Dim con As New MySqlConnection("Server=***; Port=**; Database=**; Uid=**;Pwd=**;")
            Dim cmd As New MySqlCommand
            Dim da As New MySqlDataAdapter
            Dim ds As New DataTable
            Dim bs As New BindingSource
            con.Open()
            cmd.Connection = con
            cmd.CommandType = CommandType.Text
            cmd.CommandText = "select * from users where username like '%Jean%'"
            da.SelectCommand = cmd
            da.Fill(ds)
            bs.DataSource = ds

            DataGridView.DataSource = bs
            DataGridView.Refresh()
            con.Close()

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
Afficher la suite 

5 réponses

Répondre au sujet
Whismeril 10526 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 13 décembre 2017 Dernière intervention - 13 août 2017 à 13:03
+1
Utile
Bonjour

un controle appartient au thread principal et un thread secondaire n'a pas le droit de les modifier.
Imagine que tu sois en train d'écrire dans le datagridview et qu'en même temps le thread modifie la case que tu saisis, il va y avoir conflit.

D'autre part lancer un thread via un timer n'est pas judicieux.
Si ta base de données est tellement grande que la lecture dure plus de 2 secondes, que va t il se passer?
Il faut que le thread boucle sur lui même, et éventuellement gère son temps.

MsgBox(ex.Message) 
n'est pas une instruction VB.Net, mais une tolérance de Microsoft d'importer certains codes VB6. Dans 99.99% des cas, cela fonctionne, mais quand il y a un bug il est difficile de trouver pourquoi. C'est pourquoi on conseille d'enlever l'import à VisualBasic de tout projet VB.net, voir ici. D'autre part MessageBox étant aussi un contrôle, il y aura peut être aussi une erreur cross thread.

L'utilité d'un BindingSource est de partager une source de données et d'en mettre en forme l'affichage.
Dim bs As New BindingSource
con.Open() 
cmd.Connection = con 
cmd.CommandType = CommandType.Text 
cmd.CommandText = "select * from users where username like '%Jean%'" 
da.SelectCommand = cmd 
da.Fill(ds) 
bs.DataSource = ds 

DataGridView.DataSource = bs 

Là tu crées un BindingSource uniquement à destination du DataGridView et tu ne fais aucune mise en forme.
Tu peux donc t'en passer.

con.Open() 
cmd.Connection = con 
cmd.CommandType = CommandType.Text 
cmd.CommandText = "select * from users where username like '%Jean%'" 
da.SelectCommand = cmd 
da.Fill(ds) 

DataGridView.DataSource = ds 
ferait exactement pareil, hormis le problème de thread, tout en optimisant le temps de calcul.

Enfin, tu parles de temps réel et tu utilises un backgroundwork, qui comme son nom l'indique effectue une tache de fond, moins prioritaire.
Si tu as besoin d'un grande réactivité ça n'est peut être pas le thread le plus adapté. Par contre normalement, il permet de transmettre des données sans Invoke.

Essaye ceci, pas testé chez moi, je n'ai pas mysql.
        Private Sub f1f58e8c06b2a61ce13e0c0aa9473a72()

            AddHandler bgw.DoWork, AddressOf bgw_DoWork
            bgw.WorkerReportsProgress = True 'autorise d'envoyer une progression, un rapport, un resultat intermédiaire
            AddHandler bgw.ProgressChanged, AddressOf bgw_ProgressChanged 'abonement à la progression
            AddHandler bgw.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted 'abonoment au signalement de la fin de progression
            bgw.RunWorkerAsync( { "select * from users where username like '%Jean%'", "select * from users where username like '%Jean%'" }) 'lance le backgroundworker avec les infos de connection et de recherche en paramètre
        End Sub

        Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)

             Try
            Dim tableau() As String = CType(e.Argument, String ())

            Dim con As New MySqlConnection(tableau(0))
            Dim cmd As New MySqlCommand()
            Dim da As New MySqlDataAdapter()

                Dim ds As New DataTable()


                Do
                    con.Open()
                    cmd.Connection = con
                    cmd.CommandType = CommandType.Text
                    cmd.CommandText = tableau(1)
                    da.SelectCommand = cmd
                    da.Fill(ds)

                    bgw.ReportProgress(0, da) 'j'envoie une progression à 0% et l'état de da à cet instant

                    con.Close()

                    Thread.Sleep(2000) 'je fais une pause de 2 secondes
                Loop While True


            Catch ex As Exception
                e.Result = ex 'je place l'erreur en resultat du backgroundworker
            End Try

        End Sub

        Private Sub bgw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
            Dim da As DataTable = TryCast(e.UserState, DataTable)
            If da Is Nothing Then
                Return ' l'objet UserState n'est pas un DataTable
            End If

            dataGridView1.DataSource = da
        End Sub

        Private Sub bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
            Dim erreur As Exception = TryCast(e.Result, Exception)

            If erreur Is Nothing Then
                Return
            End If

            MessageBox.Show(erreur.Message)
        End Sub


Ce backgroundworker ne s'arretra de lui même que s'il y a une erreur car j'ai mis
Loop While True
.
Il est possible qu'il y ait encore des erreurs cross thread (si par exemple ce code est dans un autre projet que celui de la form).
Il faudra alors passer par de Invoke.
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de Whismeril
Whismeril 10526 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 13 décembre 2017 Dernière intervention - 13 août 2017 à 15:17
+1
Utile
De rien
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de Whismeril
NHenry 14002 Messages postés vendredi 14 mars 2003Date d'inscriptionModérateurStatut 12 décembre 2017 Dernière intervention - 13 août 2017 à 12:40
0
Utile
1
Tu peux mettre ton code de màj dans une fonction et dans ton appelant :
if (me.InvokeRequired) then
me.Invoke(MaFonction)
else
MaFonction
End if


Ou un truc du genre (pas l'EDI chez moi)
Whismeril 10526 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 13 décembre 2017 Dernière intervention - 13 août 2017 à 13:05
Salut Nhenry, j'ai été tellement long à écrire ma réponse que je n'ai pas vu la tienne.
Il faudrait probablement passer par un dispatcher, mais je penser que les outils du backgroundworker sont suffisants.
Commenter la réponse de NHenry
f1f58e8c06b2a61ce13e0c0aa9473a72 - 13 août 2017 à 14:42
0
Utile
Merci de vos réponses, tout particulièrement Whismeril pour ton aide, ton code n'afficher rien dans la datagridview, mais en bidouillent impeut, je m'en suis finalement sortis, je te remercie pour ton aide! Bonne journée !
Commenter la réponse de f1f58e8c06b2a61ce13e0c0aa9473a72

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.