Opération inter-threads non valide

Résolu
f1f58e8c06b2a61ce13e0c0aa9473a72 - Modifié le 13 août 2017 à 12:29
Whismeril Messages postés 19020 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 15 avril 2024 - 12 août 2020 à 13:28
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

4 réponses

Whismeril Messages postés 19020 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 15 avril 2024 656
13 août 2017 à 13:03
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.
1
Rejoignez-nous